Verification with QrCode: ensure there is a Camera to scan QrCodes.

This commit is contained in:
Benoit Marty 2020-02-27 17:03:48 +01:00
parent 9f28738fd6
commit a914381090
7 changed files with 134 additions and 56 deletions

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("DEPRECATION")
package im.vector.riotx.core.hardware
import android.content.Context
import android.hardware.Camera
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Build
import javax.inject.Inject
class HardwareInfo @Inject constructor(
private val context: Context
) {
/**
* Tell if the device has a back (or external) camera
*/
fun hasBackCamera(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return Camera.getNumberOfCameras() > 0
}
val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager? ?: return Camera.getNumberOfCameras() > 0
return manager.cameraIdList.any {
val lensFacing = manager.getCameraCharacteristics(it).get(CameraCharacteristics.LENS_FACING)
lensFacing == CameraCharacteristics.LENS_FACING_BACK || lensFacing == CameraCharacteristics.LENS_FACING_EXTERNAL
}
}
}

View File

@ -1,29 +0,0 @@
/*
* 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.matrix.android.api.session.crypto.verification.VerificationMethod
val supportedVerificationMethods =
listOf(
// RiotX supports SAS verification
VerificationMethod.SAS,
// RiotX is able to show QR codes
VerificationMethod.QR_CODE_SHOW,
// RiotX is able to scan QR codes
VerificationMethod.QR_CODE_SCAN
)

View File

@ -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.features.crypto.verification
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
import im.vector.riotx.core.hardware.HardwareInfo
import timber.log.Timber
import javax.inject.Inject
class SupportedVerificationMethodsProvider @Inject constructor(
private val hardwareInfo: HardwareInfo
) {
/**
* Provide the list of supported method by RiotX, with or without the QR_CODE_SCAN, depending if a back camera
* is available
*/
fun provide(): List<VerificationMethod> {
return mutableListOf(
// RiotX supports SAS verification
VerificationMethod.SAS,
// RiotX is able to show QR codes
VerificationMethod.QR_CODE_SHOW)
.apply {
if (hardwareInfo.hasBackCamera()) {
// RiotX is able to scan QR codes, and a Camera is available
add(VerificationMethod.QR_CODE_SCAN)
} else {
// This quite uncommon
Timber.w("No back Camera detected")
}
}
}
}

View File

@ -63,9 +63,11 @@ data class VerificationBottomSheetViewState(
val isMe: Boolean = false
) : MvRxState
class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: VerificationBottomSheetViewState,
@Assisted args: VerificationBottomSheet.VerificationArgs,
private val session: Session)
class VerificationBottomSheetViewModel @AssistedInject constructor(
@Assisted initialState: VerificationBottomSheetViewState,
@Assisted args: VerificationBottomSheet.VerificationArgs,
private val session: Session,
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider)
: VectorViewModel<VerificationBottomSheetViewState, VerificationAction, VerificationBottomSheetViewEvents>(initialState),
VerificationService.Listener {
@ -116,9 +118,11 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
if (autoReady) {
// TODO, can I be here in DM mode? in this case should test if roomID is null?
session.cryptoService().verificationService()
.readyPendingVerification(supportedVerificationMethods,
.readyPendingVerification(
supportedVerificationMethodsProvider.provide(),
pr!!.otherUserId,
pr.transactionId ?: "")
pr.transactionId ?: ""
)
}
}
@ -173,7 +177,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
session
.cryptoService()
.verificationService()
.requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, data, pendingLocalId)
.requestKeyVerificationInDMs(supportedVerificationMethodsProvider.provide(), otherUserId, data, pendingLocalId)
)
)
}
@ -191,7 +195,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
pendingRequest = Success(session
.cryptoService()
.verificationService()
.requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, roomId)
.requestKeyVerificationInDMs(supportedVerificationMethodsProvider.provide(), otherUserId, roomId)
)
)
}
@ -362,9 +366,11 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
// auto ready in this case, as we are waiting
// TODO, can I be here in DM mode? in this case should test if roomID is null?
session.cryptoService().verificationService()
.readyPendingVerification(supportedVerificationMethods,
.readyPendingVerification(
supportedVerificationMethodsProvider.provide(),
pr.otherUserId,
pr.transactionId ?: "")
pr.transactionId ?: ""
)
}
// Use this one!

View File

@ -65,7 +65,7 @@ import im.vector.riotx.core.resources.UserPreferencesProvider
import im.vector.riotx.core.utils.subscribeLogError
import im.vector.riotx.features.command.CommandParser
import im.vector.riotx.features.command.ParsedCommand
import im.vector.riotx.features.crypto.verification.supportedVerificationMethods
import im.vector.riotx.features.crypto.verification.SupportedVerificationMethodsProvider
import im.vector.riotx.features.home.room.detail.composer.rainbow.RainbowGenerator
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
import im.vector.riotx.features.home.room.typing.TypingHelper
@ -81,13 +81,15 @@ import java.io.File
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: RoomDetailViewState,
userPreferencesProvider: UserPreferencesProvider,
private val vectorPreferences: VectorPreferences,
private val stringProvider: StringProvider,
private val typingHelper: TypingHelper,
private val rainbowGenerator: RainbowGenerator,
private val session: Session
class RoomDetailViewModel @AssistedInject constructor(
@Assisted initialState: RoomDetailViewState,
userPreferencesProvider: UserPreferencesProvider,
private val vectorPreferences: VectorPreferences,
private val stringProvider: StringProvider,
private val typingHelper: TypingHelper,
private val rainbowGenerator: RainbowGenerator,
private val session: Session,
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
private val room = session.getRoom(initialState.roomId)!!
@ -421,7 +423,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
session
.cryptoService()
.verificationService()
.requestKeyVerificationInDMs(supportedVerificationMethods, slashCommandResult.userId, room.roomId)
.requestKeyVerificationInDMs(supportedVerificationMethodsProvider.provide(), slashCommandResult.userId, room.roomId)
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
popDraft()
}
@ -828,7 +830,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
private fun handleAcceptVerification(action: RoomDetailAction.AcceptVerificationRequest) {
Timber.v("## SAS handleAcceptVerification ${action.otherUserId}, roomId:${room.roomId}, txId:${action.transactionId}")
if (session.cryptoService().verificationService().readyPendingVerificationInDMs(
supportedVerificationMethods,
supportedVerificationMethodsProvider.provide(),
action.otherUserId,
room.roomId,
action.transactionId)) {

View File

@ -24,7 +24,6 @@ import androidx.core.app.ActivityOptionsCompat
import androidx.core.app.TaskStackBuilder
import androidx.core.view.ViewCompat
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R
@ -35,6 +34,7 @@ import im.vector.riotx.core.utils.toast
import im.vector.riotx.features.createdirect.CreateDirectRoomActivity
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity
import im.vector.riotx.features.crypto.verification.SupportedVerificationMethodsProvider
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
import im.vector.riotx.features.debug.DebugMenuActivity
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
@ -56,7 +56,8 @@ import javax.inject.Singleton
@Singleton
class DefaultNavigator @Inject constructor(
private val sessionHolder: ActiveSessionHolder,
private val vectorPreferences: VectorPreferences
private val vectorPreferences: VectorPreferences,
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider
) : Navigator {
override fun openRoom(context: Context, roomId: String, eventId: String?, buildTask: Boolean) {
@ -85,9 +86,10 @@ class DefaultNavigator @Inject constructor(
override fun requestSessionVerification(context: Context) {
val session = sessionHolder.getSafeActiveSession() ?: return
val pr = session.cryptoService().verificationService().requestKeyVerification(
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
supportedVerificationMethodsProvider.provide(),
session.myUserId,
session.cryptoService().getUserDevices(session.myUserId).map { it.deviceId })
session.cryptoService().getUserDevices(session.myUserId).map { it.deviceId }
)
if (context is VectorBaseActivity) {
VerificationBottomSheet.withArgs(
roomId = null,

View File

@ -40,7 +40,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.crypto.verification.supportedVerificationMethods
import im.vector.riotx.features.crypto.verification.SupportedVerificationMethodsProvider
data class DevicesViewState(
val myDeviceId: String = "",
@ -50,8 +50,10 @@ data class DevicesViewState(
val request: Async<Unit> = Uninitialized
) : MvRxState
class DevicesViewModel @AssistedInject constructor(@Assisted initialState: DevicesViewState,
private val session: Session)
class DevicesViewModel @AssistedInject constructor(
@Assisted initialState: DevicesViewState,
private val session: Session,
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider)
: VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvents>(initialState), VerificationService.Listener {
@AssistedInject.Factory
@ -172,7 +174,9 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
}
private fun handleVerify(action: DevicesAction.VerifyMyDevice) {
val txID = session.cryptoService().verificationService().requestKeyVerification(supportedVerificationMethods, session.myUserId, listOf(action.deviceId))
val txID = session.cryptoService()
.verificationService()
.requestKeyVerification(supportedVerificationMethodsProvider.provide(), session.myUserId, listOf(action.deviceId))
_viewEvents.post(DevicesViewEvents.ShowVerifyDevice(
session.myUserId,
txID.transactionId