Merge branch 'develop' into feature/verification_unified_emoji
This commit is contained in:
commit
ccacd20428
@ -14,6 +14,8 @@ Improvements 🙌:
|
|||||||
- Cross-Signing | Setup key backup as part of SSSS bootstrapping (#1201)
|
- Cross-Signing | Setup key backup as part of SSSS bootstrapping (#1201)
|
||||||
- Cross-Signing | Gossip key backup recovery key (#1200)
|
- Cross-Signing | Gossip key backup recovery key (#1200)
|
||||||
- Show room encryption status as a bubble tile (#1078)
|
- Show room encryption status as a bubble tile (#1078)
|
||||||
|
- Cross-Signing | Restore history after recover from passphrase (#1214)
|
||||||
|
- Cross-Sign | QR code scan confirmation screens design update (#1187)
|
||||||
- Emoji Verification | It's not the same butterfly! (#1220)
|
- Emoji Verification | It's not the same butterfly! (#1220)
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
|
@ -43,6 +43,7 @@ sealed class VerificationTxState {
|
|||||||
|
|
||||||
// Will be used to ask the user if the other user has correctly scanned
|
// Will be used to ask the user if the other user has correctly scanned
|
||||||
object QrScannedByOther : VerificationQrTxState()
|
object QrScannedByOther : VerificationQrTxState()
|
||||||
|
object WaitingOtherReciprocateConfirm : VerificationQrTxState()
|
||||||
|
|
||||||
// Terminal states
|
// Terminal states
|
||||||
abstract class TerminalTxState : VerificationTxState()
|
abstract class TerminalTxState : VerificationTxState()
|
||||||
|
@ -646,9 +646,7 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingTransaction is SASDefaultVerificationTransaction) {
|
existingTransaction?.state = VerificationTxState.Cancelled(safeValueOf(cancelReq.code), false)
|
||||||
existingTransaction.state = VerificationTxState.Cancelled(safeValueOf(cancelReq.code), false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onRoomAcceptReceived(event: Event) {
|
private fun onRoomAcceptReceived(event: Event) {
|
||||||
@ -792,26 +790,53 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
private fun onDoneReceived(event: Event) {
|
private fun onDoneReceived(event: Event) {
|
||||||
Timber.v("## onDoneReceived")
|
Timber.v("## onDoneReceived")
|
||||||
val doneReq = event.getClearContent().toModel<KeyVerificationDone>()?.asValidObject()
|
val doneReq = event.getClearContent().toModel<KeyVerificationDone>()?.asValidObject()
|
||||||
if (doneReq == null || event.senderId != userId) {
|
if (doneReq == null || event.senderId == null) {
|
||||||
// ignore
|
// ignore
|
||||||
Timber.e("## SAS Received invalid done request")
|
Timber.e("## SAS Received invalid done request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only send gossiping request when the other sent us a done
|
handleDoneReceived(event.senderId, doneReq)
|
||||||
// We can ask without checking too much thinks (like trust), because we will check validity of secret on reception
|
|
||||||
getExistingTransaction(userId, doneReq.transactionId)
|
if (event.senderId == userId) {
|
||||||
?: getOldTransaction(userId, doneReq.transactionId)
|
// We only send gossiping request when the other sent us a done
|
||||||
?.let { vt ->
|
// We can ask without checking too much thinks (like trust), because we will check validity of secret on reception
|
||||||
val otherDeviceId = vt.otherDeviceId
|
getExistingTransaction(userId, doneReq.transactionId)
|
||||||
if (!crossSigningService.canCrossSign()) {
|
?: getOldTransaction(userId, doneReq.transactionId)
|
||||||
outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
?.let { vt ->
|
||||||
?: "*")))
|
val otherDeviceId = vt.otherDeviceId
|
||||||
outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
if (!crossSigningService.canCrossSign()) {
|
||||||
|
outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
||||||
|
?: "*")))
|
||||||
|
outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
||||||
|
?: "*")))
|
||||||
|
}
|
||||||
|
outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
||||||
?: "*")))
|
?: "*")))
|
||||||
}
|
}
|
||||||
outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleDoneReceived(senderId: String, doneReq: ValidVerificationDone) {
|
||||||
|
Timber.v("## SAS Done receieved $doneReq")
|
||||||
|
val existing = getExistingTransaction(senderId, doneReq.transactionId)
|
||||||
|
if (existing == null) {
|
||||||
|
Timber.e("## SAS Received invalid Done request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (existing is DefaultQrCodeVerificationTransaction) {
|
||||||
|
existing.onDoneReceived()
|
||||||
|
} else {
|
||||||
|
// SAS do not care for now?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now transactions are udated, let's also update Requests
|
||||||
|
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneReq.transactionId }
|
||||||
|
if (existingRequest == null) {
|
||||||
|
Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updatePendingRequest(existingRequest.copy(isSuccessful = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onRoomDoneReceived(event: Event) {
|
private fun onRoomDoneReceived(event: Event) {
|
||||||
@ -993,14 +1018,14 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDoneReceived(senderId: String, doneInfo: ValidVerificationDone) {
|
// private fun handleDoneReceived(senderId: String, doneInfo: ValidVerificationDone) {
|
||||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneInfo.transactionId }
|
// val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneInfo.transactionId }
|
||||||
if (existingRequest == null) {
|
// if (existingRequest == null) {
|
||||||
Timber.e("## SAS Received Done for unknown request txId:${doneInfo.transactionId}")
|
// Timber.e("## SAS Received Done for unknown request txId:${doneInfo.transactionId}")
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
updatePendingRequest(existingRequest.copy(isSuccessful = true))
|
// updatePendingRequest(existingRequest.copy(isSuccessful = true))
|
||||||
}
|
// }
|
||||||
|
|
||||||
// TODO All this methods should be delegated to a TransactionStore
|
// TODO All this methods should be delegated to a TransactionStore
|
||||||
override fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction? {
|
override fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction? {
|
||||||
|
@ -57,7 +57,7 @@ internal abstract class DefaultVerificationTransaction(
|
|||||||
|
|
||||||
protected fun trust(canTrustOtherUserMasterKey: Boolean,
|
protected fun trust(canTrustOtherUserMasterKey: Boolean,
|
||||||
toVerifyDeviceIds: List<String>,
|
toVerifyDeviceIds: List<String>,
|
||||||
eventuallyMarkMyMasterKeyAsTrusted: Boolean) {
|
eventuallyMarkMyMasterKeyAsTrusted: Boolean, autoDone : Boolean = true) {
|
||||||
Timber.d("## Verification: trust ($otherUserId,$otherDeviceId) , verifiedDevices:$toVerifyDeviceIds")
|
Timber.d("## Verification: trust ($otherUserId,$otherDeviceId) , verifiedDevices:$toVerifyDeviceIds")
|
||||||
Timber.d("## Verification: trust Mark myMSK trusted $eventuallyMarkMyMasterKeyAsTrusted")
|
Timber.d("## Verification: trust Mark myMSK trusted $eventuallyMarkMyMasterKeyAsTrusted")
|
||||||
|
|
||||||
@ -97,14 +97,9 @@ internal abstract class DefaultVerificationTransaction(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
state = VerificationTxState.Verified
|
if (autoDone) {
|
||||||
|
state = VerificationTxState.Verified
|
||||||
transport.done(transactionId) {
|
transport.done(transactionId) {}
|
||||||
// if (otherUserId == userId && !crossSigningService.canCrossSign()) {
|
|
||||||
// outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
|
|
||||||
// outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
|
|
||||||
// outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
internal interface VerificationInfoDone : VerificationInfo<ValidVerificationInfoDone> {
|
import im.vector.matrix.android.api.session.room.model.message.ValidVerificationDone
|
||||||
|
|
||||||
override fun asValidObject(): ValidVerificationInfoDone? {
|
internal interface VerificationInfoDone : VerificationInfo<ValidVerificationDone> {
|
||||||
|
|
||||||
|
override fun asValidObject(): ValidVerificationDone? {
|
||||||
val validTransactionId = transactionId?.takeIf { it.isNotEmpty() } ?: return null
|
val validTransactionId = transactionId?.takeIf { it.isNotEmpty() } ?: return null
|
||||||
return ValidVerificationInfoDone(validTransactionId)
|
return ValidVerificationDone(validTransactionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class ValidVerificationInfoDone(val transactionId: String)
|
|
||||||
|
@ -187,9 +187,12 @@ internal class DefaultQrCodeVerificationTransaction(
|
|||||||
// qrCodeData.sharedSecret will be used to send the start request
|
// qrCodeData.sharedSecret will be used to send the start request
|
||||||
start(otherQrCodeData.sharedSecret)
|
start(otherQrCodeData.sharedSecret)
|
||||||
|
|
||||||
trust(canTrustOtherUserMasterKey,
|
trust(
|
||||||
toVerifyDeviceIds.distinct(),
|
canTrustOtherUserMasterKey = canTrustOtherUserMasterKey,
|
||||||
eventuallyMarkMyMasterKeyAsTrusted = true)
|
toVerifyDeviceIds = toVerifyDeviceIds.distinct(),
|
||||||
|
eventuallyMarkMyMasterKeyAsTrusted = true,
|
||||||
|
autoDone = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun start(remoteSecret: String, onDone: (() -> Unit)? = null) {
|
private fun start(remoteSecret: String, onDone: (() -> Unit)? = null) {
|
||||||
@ -199,6 +202,7 @@ internal class DefaultQrCodeVerificationTransaction(
|
|||||||
throw IllegalStateException("Interactive Key verification already started")
|
throw IllegalStateException("Interactive Key verification already started")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = VerificationTxState.Started
|
||||||
val startMessage = transport.createStartForQrCode(
|
val startMessage = transport.createStartForQrCode(
|
||||||
deviceId,
|
deviceId,
|
||||||
transactionId,
|
transactionId,
|
||||||
@ -208,7 +212,7 @@ internal class DefaultQrCodeVerificationTransaction(
|
|||||||
transport.sendToOther(
|
transport.sendToOther(
|
||||||
EventType.KEY_VERIFICATION_START,
|
EventType.KEY_VERIFICATION_START,
|
||||||
startMessage,
|
startMessage,
|
||||||
VerificationTxState.Started,
|
VerificationTxState.WaitingOtherReciprocateConfirm,
|
||||||
CancelCode.User,
|
CancelCode.User,
|
||||||
onDone
|
onDone
|
||||||
)
|
)
|
||||||
@ -244,6 +248,15 @@ internal class DefaultQrCodeVerificationTransaction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onDoneReceived() {
|
||||||
|
if (state != VerificationTxState.WaitingOtherReciprocateConfirm) {
|
||||||
|
cancel(CancelCode.UnexpectedMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state = VerificationTxState.Verified
|
||||||
|
transport.done(transactionId) {}
|
||||||
|
}
|
||||||
|
|
||||||
override fun otherUserScannedMyQrCode() {
|
override fun otherUserScannedMyQrCode() {
|
||||||
when (qrCodeData) {
|
when (qrCodeData) {
|
||||||
is QrCodeData.VerifyingAnotherUser -> {
|
is QrCodeData.VerifyingAnotherUser -> {
|
||||||
@ -265,6 +278,6 @@ internal class DefaultQrCodeVerificationTransaction(
|
|||||||
override fun otherUserDidNotScannedMyQrCode() {
|
override fun otherUserDidNotScannedMyQrCode() {
|
||||||
// What can I do then?
|
// What can I do then?
|
||||||
// At least remove the transaction...
|
// At least remove the transaction...
|
||||||
state = VerificationTxState.Cancelled(CancelCode.MismatchedKeys, true)
|
cancel(CancelCode.MismatchedKeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFrag
|
|||||||
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||||
import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
|
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.emoji.VerificationEmojiCodeFragment
|
||||||
|
import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment
|
||||||
import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
|
import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
|
||||||
import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
|
import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
|
||||||
import im.vector.riotx.features.grouplist.GroupListFragment
|
import im.vector.riotx.features.grouplist.GroupListFragment
|
||||||
@ -339,6 +340,11 @@ interface FragmentModule {
|
|||||||
@FragmentKey(VerificationQrScannedByOtherFragment::class)
|
@FragmentKey(VerificationQrScannedByOtherFragment::class)
|
||||||
fun bindVerificationQrScannedByOtherFragment(fragment: VerificationQrScannedByOtherFragment): Fragment
|
fun bindVerificationQrScannedByOtherFragment(fragment: VerificationQrScannedByOtherFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(VerificationQRWaitingFragment::class)
|
||||||
|
fun bindVerificationQRWaitingFragment(fragment: VerificationQRWaitingFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(VerificationConclusionFragment::class)
|
@FragmentKey(VerificationConclusionFragment::class)
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.riotx.features.crypto.quads
|
package im.vector.riotx.features.crypto.quads
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
@ -34,9 +35,9 @@ import im.vector.riotx.core.platform.VectorViewModel
|
|||||||
import im.vector.riotx.core.platform.WaitingViewData
|
import im.vector.riotx.core.platform.WaitingViewData
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
data class SharedSecureStorageViewState(
|
data class SharedSecureStorageViewState(
|
||||||
@ -77,7 +78,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun handleSubmitPassphrase(action: SharedSecureStorageAction.SubmitPassphrase) {
|
private fun handleSubmitPassphrase(action: SharedSecureStorageAction.SubmitPassphrase) {
|
||||||
val decryptedSecretMap = HashMap<String, String>()
|
val decryptedSecretMap = HashMap<String, String>()
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
runCatching {
|
runCatching {
|
||||||
_viewEvents.post(SharedSecureStorageViewEvent.ShowModalLoading)
|
_viewEvents.post(SharedSecureStorageViewEvent.ShowModalLoading)
|
||||||
val passphrase = action.passphrase
|
val passphrase = action.passphrase
|
||||||
@ -116,14 +117,18 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
args.requestedSecrets.forEach {
|
args.requestedSecrets.forEach {
|
||||||
val res = awaitCallback<String> { callback ->
|
if (session.getAccountDataEvent(it) != null) {
|
||||||
session.sharedSecretStorageService.getSecret(
|
val res = awaitCallback<String> { callback ->
|
||||||
name = it,
|
session.sharedSecretStorageService.getSecret(
|
||||||
keyId = keyInfo.id,
|
name = it,
|
||||||
secretKey = keySpec,
|
keyId = keyInfo.id,
|
||||||
callback = callback)
|
secretKey = keySpec,
|
||||||
|
callback = callback)
|
||||||
|
}
|
||||||
|
decryptedSecretMap[it] = res
|
||||||
|
} else {
|
||||||
|
Timber.w("## Cannot find secret $it in SSSS, skip")
|
||||||
}
|
}
|
||||||
decryptedSecretMap[it] = res
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.fold({
|
}.fold({
|
||||||
|
@ -32,6 +32,7 @@ import com.airbnb.mvrx.MvRx
|
|||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||||
@ -49,6 +50,7 @@ import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFrag
|
|||||||
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||||
import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
|
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.emoji.VerificationEmojiCodeFragment
|
||||||
|
import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment
|
||||||
import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
|
import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
|
||||||
import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
|
import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
@ -108,7 +110,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
startActivityForResult(SharedSecureStorageActivity.newIntent(
|
startActivityForResult(SharedSecureStorageActivity.newIntent(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
null, // use default key
|
null, // use default key
|
||||||
listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME),
|
listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME),
|
||||||
SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
|
SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
|
||||||
), SECRET_REQUEST_CODE)
|
), SECRET_REQUEST_CODE)
|
||||||
}
|
}
|
||||||
@ -243,6 +245,13 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
showFragment(VerificationQrScannedByOtherFragment::class, Bundle())
|
showFragment(VerificationQrScannedByOtherFragment::class, Bundle())
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
is VerificationTxState.Started,
|
||||||
|
is VerificationTxState.WaitingOtherReciprocateConfirm -> {
|
||||||
|
showFragment(VerificationQRWaitingFragment::class, Bundle().apply {
|
||||||
|
putParcelable(MvRx.KEY_ARG, VerificationQRWaitingFragment.Args(state.isMe, state.otherUserMxItem?.getBestName() ?: ""))
|
||||||
|
})
|
||||||
|
return@withState
|
||||||
|
}
|
||||||
is VerificationTxState.Verified -> {
|
is VerificationTxState.Verified -> {
|
||||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe))
|
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe))
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.riotx.features.crypto.verification
|
package im.vector.riotx.features.crypto.verification
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
@ -28,6 +29,7 @@ import com.squareup.inject.assisted.Assisted
|
|||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||||
@ -46,8 +48,15 @@ import im.vector.matrix.android.api.util.MatrixItem
|
|||||||
import im.vector.matrix.android.api.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
|
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.isVerified
|
import im.vector.matrix.android.internal.crypto.crosssigning.isVerified
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||||
|
import im.vector.matrix.android.internal.util.awaitCallback
|
||||||
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.exhaustive
|
import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
data class VerificationBottomSheetViewState(
|
data class VerificationBottomSheetViewState(
|
||||||
@ -71,7 +80,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||||||
@Assisted initialState: VerificationBottomSheetViewState,
|
@Assisted initialState: VerificationBottomSheetViewState,
|
||||||
@Assisted val args: VerificationBottomSheet.VerificationArgs,
|
@Assisted val args: VerificationBottomSheet.VerificationArgs,
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider)
|
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider,
|
||||||
|
private val stringProvider: StringProvider)
|
||||||
: VectorViewModel<VerificationBottomSheetViewState, VerificationAction, VerificationBottomSheetViewEvents>(initialState),
|
: VectorViewModel<VerificationBottomSheetViewState, VerificationAction, VerificationBottomSheetViewEvents>(initialState),
|
||||||
VerificationService.Listener {
|
VerificationService.Listener {
|
||||||
|
|
||||||
@ -334,40 +344,82 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||||||
_viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore)
|
_viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore)
|
||||||
}
|
}
|
||||||
is VerificationAction.GotResultFromSsss -> {
|
is VerificationAction.GotResultFromSsss -> {
|
||||||
try {
|
handleSecretBackFromSSSS(action)
|
||||||
action.cypherData.fromBase64().inputStream().use { ins ->
|
|
||||||
val res = session.loadSecureSecret<Map<String, String>>(ins, action.alias)
|
|
||||||
val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys(
|
|
||||||
res?.get(MASTER_KEY_SSSS_NAME),
|
|
||||||
res?.get(USER_SIGNING_KEY_SSSS_NAME),
|
|
||||||
res?.get(SELF_SIGNING_KEY_SSSS_NAME)
|
|
||||||
)
|
|
||||||
if (trustResult.isVerified()) {
|
|
||||||
// Sign this device and upload the signature
|
|
||||||
session.sessionParams.credentials.deviceId?.let { deviceId ->
|
|
||||||
session.cryptoService()
|
|
||||||
.crossSigningService().trustDevice(deviceId, object : MatrixCallback<Unit> {
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
Timber.w(failure, "Failed to sign my device after recovery")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(verifiedFromPrivateKeys = true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// POP UP something
|
|
||||||
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError("Failed to import keys"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSecretBackFromSSSS(action: VerificationAction.GotResultFromSsss) {
|
||||||
|
try {
|
||||||
|
action.cypherData.fromBase64().inputStream().use { ins ->
|
||||||
|
val res = session.loadSecureSecret<Map<String, String>>(ins, action.alias)
|
||||||
|
val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys(
|
||||||
|
res?.get(MASTER_KEY_SSSS_NAME),
|
||||||
|
res?.get(USER_SIGNING_KEY_SSSS_NAME),
|
||||||
|
res?.get(SELF_SIGNING_KEY_SSSS_NAME)
|
||||||
|
)
|
||||||
|
if (trustResult.isVerified()) {
|
||||||
|
// Sign this device and upload the signature
|
||||||
|
session.sessionParams.credentials.deviceId?.let { deviceId ->
|
||||||
|
session.cryptoService()
|
||||||
|
.crossSigningService().trustDevice(deviceId, object : MatrixCallback<Unit> {
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
Timber.w(failure, "Failed to sign my device after recovery")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setState {
|
||||||
|
copy(verifiedFromPrivateKeys = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to get keybackup key
|
||||||
|
} else {
|
||||||
|
// POP UP something
|
||||||
|
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(stringProvider.getString(R.string.error_failed_to_import_keys)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// try the keybackup
|
||||||
|
tentativeRestoreBackup(res)
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tentativeRestoreBackup(res: Map<String, String>?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val secret = res?.get(KEYBACKUP_SECRET_SSSS_NAME) ?: return@launch Unit.also {
|
||||||
|
Timber.v("## Keybackup secret not restored from SSSS")
|
||||||
|
}
|
||||||
|
|
||||||
|
val version = awaitCallback<KeysVersionResult?> {
|
||||||
|
session.cryptoService().keysBackupService().getCurrentVersion(it)
|
||||||
|
} ?: return@launch
|
||||||
|
|
||||||
|
awaitCallback<ImportRoomKeysResult> {
|
||||||
|
session.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(
|
||||||
|
version,
|
||||||
|
computeRecoveryKey(secret.fromBase64()),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
awaitCallback<Unit> {
|
||||||
|
session.cryptoService().keysBackupService().trustKeysBackupVersion(version, true, it)
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
// Just ignore for now
|
||||||
|
Timber.v("## Failed to restore backup after SSSS recovery")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun transactionCreated(tx: VerificationTransaction) {
|
override fun transactionCreated(tx: VerificationTransaction) {
|
||||||
transactionUpdated(tx)
|
transactionUpdated(tx)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotx.features.crypto.verification.qrconfirmation
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.EpoxyController
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.resources.ColorProvider
|
||||||
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
|
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
|
||||||
|
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||||
|
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class VerificationQRWaitingController @Inject constructor(
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val colorProvider: ColorProvider
|
||||||
|
) : EpoxyController() {
|
||||||
|
|
||||||
|
private var args: VerificationQRWaitingFragment.Args? = null
|
||||||
|
|
||||||
|
fun update(args: VerificationQRWaitingFragment.Args) {
|
||||||
|
this.args = args
|
||||||
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildModels() {
|
||||||
|
val params = args ?: return
|
||||||
|
|
||||||
|
bottomSheetVerificationNoticeItem {
|
||||||
|
id("notice")
|
||||||
|
apply {
|
||||||
|
notice(stringProvider.getString(R.string.qr_code_scanned_verif_waiting_notice))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomSheetVerificationBigImageItem {
|
||||||
|
id("image")
|
||||||
|
imageRes(R.drawable.ic_shield_trusted)
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomSheetVerificationWaitingItem {
|
||||||
|
id("waiting")
|
||||||
|
title(stringProvider.getString(R.string.qr_code_scanned_verif_waiting, params.otherUserName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotx.features.crypto.verification.qrconfirmation
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.view.View
|
||||||
|
import com.airbnb.mvrx.MvRx
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class VerificationQRWaitingFragment @Inject constructor(
|
||||||
|
val controller: VerificationQRWaitingController
|
||||||
|
) : VectorBaseFragment() {
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Args(
|
||||||
|
val isMe: Boolean,
|
||||||
|
val otherUserName: String
|
||||||
|
) : Parcelable
|
||||||
|
|
||||||
|
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupRecyclerView()
|
||||||
|
(arguments?.getParcelable(MvRx.KEY_ARG) as? Args)?.let {
|
||||||
|
controller.update(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
bottomSheetVerificationRecyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,9 @@ import im.vector.riotx.R
|
|||||||
import im.vector.riotx.core.epoxy.dividerItem
|
import im.vector.riotx.core.epoxy.dividerItem
|
||||||
import im.vector.riotx.core.resources.ColorProvider
|
import im.vector.riotx.core.resources.ColorProvider
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
|
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewState
|
||||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
||||||
|
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
|
||||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -32,33 +34,37 @@ class VerificationQrScannedByOtherController @Inject constructor(
|
|||||||
|
|
||||||
var listener: Listener? = null
|
var listener: Listener? = null
|
||||||
|
|
||||||
init {
|
private var viewState: VerificationBottomSheetViewState? = null
|
||||||
|
|
||||||
|
fun update(viewState: VerificationBottomSheetViewState) {
|
||||||
|
this.viewState = viewState
|
||||||
requestModelBuild()
|
requestModelBuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildModels() {
|
override fun buildModels() {
|
||||||
|
val state = viewState ?: return
|
||||||
|
|
||||||
bottomSheetVerificationNoticeItem {
|
bottomSheetVerificationNoticeItem {
|
||||||
id("notice")
|
id("notice")
|
||||||
notice(stringProvider.getString(R.string.qr_code_scanned_by_other_notice))
|
apply {
|
||||||
|
if (state.isMe) {
|
||||||
|
notice(stringProvider.getString(R.string.qr_code_scanned_self_verif_notice))
|
||||||
|
} else {
|
||||||
|
val name = state.otherUserMxItem?.getBestName() ?: ""
|
||||||
|
notice(stringProvider.getString(R.string.qr_code_scanned_by_other_notice, name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomSheetVerificationBigImageItem {
|
||||||
|
id("image")
|
||||||
|
imageRes(R.drawable.ic_shield_trusted)
|
||||||
}
|
}
|
||||||
|
|
||||||
dividerItem {
|
dividerItem {
|
||||||
id("sep0")
|
id("sep0")
|
||||||
}
|
}
|
||||||
|
|
||||||
bottomSheetVerificationActionItem {
|
|
||||||
id("confirm")
|
|
||||||
title(stringProvider.getString(R.string.qr_code_scanned_by_other_yes))
|
|
||||||
titleColor(colorProvider.getColor(R.color.riotx_accent))
|
|
||||||
iconRes(R.drawable.ic_check_on)
|
|
||||||
iconColor(colorProvider.getColor(R.color.riotx_accent))
|
|
||||||
listener { listener?.onUserConfirmsQrCodeScanned() }
|
|
||||||
}
|
|
||||||
|
|
||||||
dividerItem {
|
|
||||||
id("sep1")
|
|
||||||
}
|
|
||||||
|
|
||||||
bottomSheetVerificationActionItem {
|
bottomSheetVerificationActionItem {
|
||||||
id("deny")
|
id("deny")
|
||||||
title(stringProvider.getString(R.string.qr_code_scanned_by_other_no))
|
title(stringProvider.getString(R.string.qr_code_scanned_by_other_no))
|
||||||
@ -67,6 +73,19 @@ class VerificationQrScannedByOtherController @Inject constructor(
|
|||||||
iconColor(colorProvider.getColor(R.color.vector_error_color))
|
iconColor(colorProvider.getColor(R.color.vector_error_color))
|
||||||
listener { listener?.onUserDeniesQrCodeScanned() }
|
listener { listener?.onUserDeniesQrCodeScanned() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dividerItem {
|
||||||
|
id("sep1")
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomSheetVerificationActionItem {
|
||||||
|
id("confirm")
|
||||||
|
title(stringProvider.getString(R.string.qr_code_scanned_by_other_yes))
|
||||||
|
titleColor(colorProvider.getColor(R.color.riotx_accent))
|
||||||
|
iconRes(R.drawable.ic_check_on)
|
||||||
|
iconColor(colorProvider.getColor(R.color.riotx_accent))
|
||||||
|
listener { listener?.onUserConfirmsQrCodeScanned() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
|
@ -18,6 +18,7 @@ package im.vector.riotx.features.crypto.verification.qrconfirmation
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.airbnb.mvrx.parentFragmentViewModel
|
import com.airbnb.mvrx.parentFragmentViewModel
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.cleanup
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
import im.vector.riotx.core.extensions.configureWith
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
@ -37,10 +38,13 @@ class VerificationQrScannedByOtherFragment @Inject constructor(
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invalidate() = withState(sharedViewModel) { state ->
|
||||||
|
controller.update(state)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
bottomSheetVerificationRecyclerView.cleanup()
|
bottomSheetVerificationRecyclerView.cleanup()
|
||||||
controller.listener = null
|
controller.listener = null
|
||||||
|
@ -2166,7 +2166,6 @@ Abisua: Fitxategi hau ezabatu daiteke aplikazioa desinstalatzen bada.</string>
|
|||||||
|
|
||||||
<string name="a11y_qr_code_for_verification">QR kodea</string>
|
<string name="a11y_qr_code_for_verification">QR kodea</string>
|
||||||
|
|
||||||
<string name="qr_code_scanned_by_other_notice">Beste erabiltzaileak QR kodea ongi eskaneatu du\?</string>
|
|
||||||
<string name="qr_code_scanned_by_other_yes">Bai</string>
|
<string name="qr_code_scanned_by_other_yes">Bai</string>
|
||||||
<string name="qr_code_scanned_by_other_no">Ez</string>
|
<string name="qr_code_scanned_by_other_no">Ez</string>
|
||||||
|
|
||||||
|
@ -2174,7 +2174,6 @@ Si vous n’avez pas configuré de nouvelle méthode de récupération, un attaq
|
|||||||
|
|
||||||
<string name="a11y_qr_code_for_verification">Code QR</string>
|
<string name="a11y_qr_code_for_verification">Code QR</string>
|
||||||
|
|
||||||
<string name="qr_code_scanned_by_other_notice">L’autre utilisateur a-t-il bien scanné le code QR \?</string>
|
|
||||||
<string name="qr_code_scanned_by_other_yes">Oui</string>
|
<string name="qr_code_scanned_by_other_yes">Oui</string>
|
||||||
<string name="qr_code_scanned_by_other_no">Non</string>
|
<string name="qr_code_scanned_by_other_no">Non</string>
|
||||||
|
|
||||||
|
@ -2169,7 +2169,6 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
|
|||||||
|
|
||||||
<string name="a11y_qr_code_for_verification">QR kód</string>
|
<string name="a11y_qr_code_for_verification">QR kód</string>
|
||||||
|
|
||||||
<string name="qr_code_scanned_by_other_notice">A másik felhasználó sikeresen beolvasta a QR kódot\?</string>
|
|
||||||
<string name="qr_code_scanned_by_other_yes">Igen</string>
|
<string name="qr_code_scanned_by_other_yes">Igen</string>
|
||||||
<string name="qr_code_scanned_by_other_no">Nem</string>
|
<string name="qr_code_scanned_by_other_no">Nem</string>
|
||||||
|
|
||||||
|
@ -2219,7 +2219,6 @@
|
|||||||
|
|
||||||
<string name="a11y_qr_code_for_verification">Codice QR</string>
|
<string name="a11y_qr_code_for_verification">Codice QR</string>
|
||||||
|
|
||||||
<string name="qr_code_scanned_by_other_notice">L\'altro utente ha scansionato correttamente il codice QR\?</string>
|
|
||||||
<string name="qr_code_scanned_by_other_yes">Sì</string>
|
<string name="qr_code_scanned_by_other_yes">Sì</string>
|
||||||
<string name="qr_code_scanned_by_other_no">No</string>
|
<string name="qr_code_scanned_by_other_no">No</string>
|
||||||
|
|
||||||
|
@ -2129,7 +2129,6 @@ Që të garantoni se s’ju shpëton gjë, thjesht mbajeni të aktivizuar mekani
|
|||||||
<string name="initialize_cross_signing">Gatit <em>CrossSigning</em></string>
|
<string name="initialize_cross_signing">Gatit <em>CrossSigning</em></string>
|
||||||
<string name="reset_cross_signing">Zeroji Kyçet</string>
|
<string name="reset_cross_signing">Zeroji Kyçet</string>
|
||||||
|
|
||||||
<string name="qr_code_scanned_by_other_notice">A e skanoi me sukses përdoruesi tjetër kodin QR\?</string>
|
|
||||||
<string name="qr_code_scanned_by_other_no">Jo</string>
|
<string name="qr_code_scanned_by_other_no">Jo</string>
|
||||||
|
|
||||||
<string name="no_connectivity_to_the_server_indicator">Humbi lidhja me shërbyesin</string>
|
<string name="no_connectivity_to_the_server_indicator">Humbi lidhja me shërbyesin</string>
|
||||||
|
@ -2119,7 +2119,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
|
|||||||
|
|
||||||
<string name="a11y_qr_code_for_verification">QR code</string>
|
<string name="a11y_qr_code_for_verification">QR code</string>
|
||||||
|
|
||||||
<string name="qr_code_scanned_by_other_notice">其他使用者是否掃苗 QR code 成功?</string>
|
|
||||||
<string name="qr_code_scanned_by_other_yes">是</string>
|
<string name="qr_code_scanned_by_other_yes">是</string>
|
||||||
<string name="qr_code_scanned_by_other_no">否</string>
|
<string name="qr_code_scanned_by_other_no">否</string>
|
||||||
|
|
||||||
|
@ -2148,7 +2148,7 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
|
|||||||
|
|
||||||
<string name="a11y_qr_code_for_verification">QR code</string>
|
<string name="a11y_qr_code_for_verification">QR code</string>
|
||||||
|
|
||||||
<string name="qr_code_scanned_by_other_notice">Did the other user successfully scan the QR code?</string>
|
<string name="qr_code_scanned_by_other_notice">Almost there! Is %s showing the same shield?</string>
|
||||||
<string name="qr_code_scanned_by_other_yes">Yes</string>
|
<string name="qr_code_scanned_by_other_yes">Yes</string>
|
||||||
<string name="qr_code_scanned_by_other_no">No</string>
|
<string name="qr_code_scanned_by_other_no">No</string>
|
||||||
|
|
||||||
|
@ -94,6 +94,13 @@
|
|||||||
<string name="encryption_unknown_algorithm_tile_description">The encryption used by this room is not supported</string>
|
<string name="encryption_unknown_algorithm_tile_description">The encryption used by this room is not supported</string>
|
||||||
|
|
||||||
<string name="room_created_summary_item">%s created and configured the room.</string>
|
<string name="room_created_summary_item">%s created and configured the room.</string>
|
||||||
|
|
||||||
|
<string name="qr_code_scanned_self_verif_notice">Almost there! Is the other device showing the same shield?</string>
|
||||||
|
<string name="qr_code_scanned_verif_waiting_notice">Almost there! Waiting for confirmation…</string>
|
||||||
|
<string name="qr_code_scanned_verif_waiting">Waiting for %s…</string>
|
||||||
|
|
||||||
|
<string name="error_failed_to_import_keys">Failed to import keys</string>
|
||||||
|
|
||||||
<!-- END Strings added by Valere -->
|
<!-- END Strings added by Valere -->
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user