merge madness ??

This commit is contained in:
Valere 2019-12-30 19:52:48 +01:00
parent 3eed9b5083
commit 3c4506cb58
28 changed files with 453 additions and 14 deletions

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.crypto.sas package im.vector.matrix.android.api.session.crypto.sas
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
/** /**
* https://matrix.org/docs/spec/client_server/r0.5.0#key-verification-framework * https://matrix.org/docs/spec/client_server/r0.5.0#key-verification-framework
@ -54,7 +55,7 @@ interface SasVerificationService {
*/ */
fun beginKeyVerification(method: String, userId: String, deviceID: String): String? fun beginKeyVerification(method: String, userId: String, deviceID: String): String?
fun requestKeyVerificationInDMs(userId: String, roomId: String, callback: MatrixCallback<String>?) fun requestKeyVerificationInDMs(userId: String, roomId: String, callback: MatrixCallback<String>?) : PendingVerificationRequest
fun beginKeyVerificationInDMs(method: String, fun beginKeyVerificationInDMs(method: String,
transactionId: String, transactionId: String,
@ -63,11 +64,16 @@ interface SasVerificationService {
otherDeviceId: String, otherDeviceId: String,
callback: MatrixCallback<String>?): String? callback: MatrixCallback<String>?): String?
fun readyPendingVerificationInDMs(otherUserId: String, roomId: String, transactionId: String)
// fun transactionUpdated(tx: SasVerificationTransaction) // fun transactionUpdated(tx: SasVerificationTransaction)
interface SasVerificationListener { interface SasVerificationListener {
fun transactionCreated(tx: SasVerificationTransaction) fun transactionCreated(tx: SasVerificationTransaction)
fun transactionUpdated(tx: SasVerificationTransaction) fun transactionUpdated(tx: SasVerificationTransaction)
fun markedAsManuallyVerified(userId: String, deviceId: String) fun markedAsManuallyVerified(userId: String, deviceId: String) {}
fun verificationRequestCreated(pr: PendingVerificationRequest) {}
fun verificationRequestUpdated(pr: PendingVerificationRequest) {}
} }
} }

View File

@ -47,4 +47,7 @@ interface SasVerificationTransaction {
* both short codes do match * both short codes do match
*/ */
fun userHasVerifiedShortCode() fun userHasVerifiedShortCode()
fun shortCodeDoNotMatch()
} }

View File

@ -73,6 +73,7 @@ object EventType {
const val KEY_VERIFICATION_MAC = "m.key.verification.mac" const val KEY_VERIFICATION_MAC = "m.key.verification.mac"
const val KEY_VERIFICATION_CANCEL = "m.key.verification.cancel" const val KEY_VERIFICATION_CANCEL = "m.key.verification.cancel"
const val KEY_VERIFICATION_DONE = "m.key.verification.done" const val KEY_VERIFICATION_DONE = "m.key.verification.done"
const val KEY_VERIFICATION_READY = "m.key.verification.ready"
// Relation Events // Relation Events
const val REACTION = "m.reaction" const val REACTION = "m.reaction"

View File

@ -89,4 +89,6 @@ interface RoomService {
fun getRoomIdByAlias(roomAlias: String, fun getRoomIdByAlias(roomAlias: String,
searchOnServer: Boolean, searchOnServer: Boolean,
callback: MatrixCallback<Optional<String>>): Cancelable callback: MatrixCallback<Optional<String>>): Cancelable
fun getExistingDirectRoomWithUser(otherUserId: String) : Room?
} }

View File

@ -0,0 +1,57 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.room.model.message
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.RelationType
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
import im.vector.matrix.android.internal.crypto.verification.MessageVerificationReadyFactory
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoReady
@JsonClass(generateAdapter = true)
internal data class MessageVerificationReadyContent(
@Json(name = "from_device") override val fromDevice: String? = null,
@Json(name = "methods") override val methods: List<String>? = null,
@Json(name = "m.relates_to") val relatesTo: RelationDefaultContent?
) : VerificationInfoReady {
override val transactionID: String?
get() = relatesTo?.eventId
override fun toEventContent() = this.toContent()
override fun isValid(): Boolean {
if (transactionID.isNullOrBlank() || methods.isNullOrEmpty() || fromDevice.isNullOrEmpty()) {
return false
}
return true
}
companion object : MessageVerificationReadyFactory {
override fun create(tid: String, methods: List<String>, fromDevice: String): VerificationInfoReady {
return MessageVerificationReadyContent(
fromDevice = fromDevice,
methods = methods,
relatesTo = RelationDefaultContent(
RelationType.REFERENCE,
tid
)
)
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto.model.rest
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoReady
/**
* Requests a key verification with another user's devices.
*/
@JsonClass(generateAdapter = true)
internal data class KeyVerificationReady(
@Json(name = "from_device") override val fromDevice: String?,
//TODO add qr?
@Json(name = "methods") override val methods: List<String>? = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
@Json(name = "transaction_id") override var transactionID: String? = null
) : SendToDeviceObject, VerificationInfoReady {
override fun toSendToDeviceObject() = this
override fun isValid(): Boolean {
return !transactionID.isNullOrBlank() && !fromDevice.isNullOrBlank() && !methods.isNullOrEmpty()
}
}

View File

@ -43,6 +43,7 @@ data class KeyVerificationStart(
companion object { companion object {
const val VERIF_METHOD_SAS = "m.sas.v1" const val VERIF_METHOD_SAS = "m.sas.v1"
const val VERIF_METHOD_SCAN = "m.qr_code.scan.v1"
} }
override fun isValid(): Boolean { override fun isValid(): Boolean {

View File

@ -33,7 +33,8 @@ internal class DefaultIncomingSASVerificationTransaction(
private val cryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
deviceFingerprint: String, deviceFingerprint: String,
transactionId: String, transactionId: String,
otherUserID: String otherUserID: String,
val autoAccept: Boolean = false
) : SASVerificationTransaction( ) : SASVerificationTransaction(
setDeviceVerificationAction, setDeviceVerificationAction,
credentials, credentials,
@ -76,6 +77,10 @@ internal class DefaultIncomingSASVerificationTransaction(
this.startReq = startReq this.startReq = startReq
state = SasVerificationTxState.OnStarted state = SasVerificationTxState.OnStarted
this.otherDeviceId = startReq.fromDevice this.otherDeviceId = startReq.fromDevice
if (autoAccept) {
performAccept()
}
} }
override fun performAccept() { override fun performAccept() {

View File

@ -75,6 +75,12 @@ internal class DefaultSasVerificationService @Inject constructor(
// map [sender : [transaction]] // map [sender : [transaction]]
private val txMap = HashMap<String, HashMap<String, VerificationTransaction>>() private val txMap = HashMap<String, HashMap<String, VerificationTransaction>>()
/**
* Map [sender: [PendingVerificationRequest]]
*/
private val pendingRequests = HashMap<String, ArrayList<PendingVerificationRequest>>()
// Event received from the sync // Event received from the sync
fun onToDeviceEvent(event: Event) { fun onToDeviceEvent(event: Event) {
GlobalScope.launch(coroutineDispatchers.crypto) { GlobalScope.launch(coroutineDispatchers.crypto) {
@ -120,6 +126,9 @@ internal class DefaultSasVerificationService @Inject constructor(
EventType.KEY_VERIFICATION_MAC -> { EventType.KEY_VERIFICATION_MAC -> {
onRoomMacReceived(event) onRoomMacReceived(event)
} }
EventType.KEY_VERIFICATION_READY -> {
onRoomReadyReceived(event)
}
EventType.KEY_VERIFICATION_DONE -> { EventType.KEY_VERIFICATION_DONE -> {
// TODO? // TODO?
} }
@ -175,6 +184,31 @@ internal class DefaultSasVerificationService @Inject constructor(
} }
} }
private fun dispatchRequestAdded(tx: PendingVerificationRequest) {
uiHandler.post {
listeners.forEach {
try {
it.verificationRequestCreated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
private fun dispatchRequestUpdated(tx: PendingVerificationRequest) {
uiHandler.post {
listeners.forEach {
try {
it.verificationRequestUpdated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
setDeviceVerificationAction.handle(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, setDeviceVerificationAction.handle(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED,
deviceID, deviceID,
@ -326,6 +360,10 @@ internal class DefaultSasVerificationService @Inject constructor(
// Ok we can create // Ok we can create
if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) { if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) {
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}") Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
// If there is a corresponding request, we can auto accept
// as we are the one requesting in first place (or we accepted the request)
val autoAccept = getExistingVerificationRequest(otherUserId)?.any { it.transactionId == startReq.transactionID }
?: false
val tx = DefaultIncomingSASVerificationTransaction( val tx = DefaultIncomingSASVerificationTransaction(
// this, // this,
setDeviceVerificationAction, setDeviceVerificationAction,
@ -333,7 +371,8 @@ internal class DefaultSasVerificationService @Inject constructor(
cryptoStore, cryptoStore,
myDeviceInfoHolder.get().myDevice.fingerprint()!!, myDeviceInfoHolder.get().myDevice.fingerprint()!!,
startReq.transactionID!!, startReq.transactionID!!,
otherUserId).also { txConfigure(it) } otherUserId,
autoAccept).also { txConfigure(it) }
addTransaction(tx) addTransaction(tx)
tx.acceptVerificationEvent(otherUserId, startReq) tx.acceptVerificationEvent(otherUserId, startReq)
} else { } else {
@ -546,6 +585,15 @@ internal class DefaultSasVerificationService @Inject constructor(
} }
} }
private fun handleReadyReceived(senderId: String, readyReq: VerificationInfoReady) {
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionID }
if (existingRequest == null) {
Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionID} fromDevice ${readyReq.fromDevice}")
return
}
updateOutgoingPendingRequest(existingRequest.copy(readyInfo = readyReq))
}
override fun getExistingTransaction(otherUser: String, tid: String): VerificationTransaction? { override fun getExistingTransaction(otherUser: String, tid: String): VerificationTransaction? {
synchronized(lock = txMap) { synchronized(lock = txMap) {
return txMap[otherUser]?.get(tid) return txMap[otherUser]?.get(tid)
@ -647,6 +695,12 @@ internal class DefaultSasVerificationService @Inject constructor(
) { ) {
this.callback = object : MatrixCallback<SendResponse> { this.callback = object : MatrixCallback<SendResponse> {
override fun onSuccess(data: SendResponse) { override fun onSuccess(data: SendResponse) {
params.event.getClearContent().toModel<MessageVerificationRequestContent>()?.let {
updateOutgoingPendingRequest(verificationRequest.copy(
transactionId = data.eventId,
requestInfo = it
))
}
callback?.onSuccess(data.eventId) callback?.onSuccess(data.eventId)
} }
@ -657,6 +711,24 @@ internal class DefaultSasVerificationService @Inject constructor(
constraints = TaskConstraints(true) constraints = TaskConstraints(true)
retryCount = 3 retryCount = 3
}.executeBy(taskExecutor) }.executeBy(taskExecutor)
return verificationRequest
}
private fun updateOutgoingPendingRequest(updated: PendingVerificationRequest) {
val requestsForUser = pendingRequests[updated.otherUserId]
?: ArrayList<PendingVerificationRequest>().also {
pendingRequests[updated.otherUserId] = it
}
val index = requestsForUser.indexOfFirst {
it.transactionId == updated.transactionId
|| it.transactionId == null && it.localID == updated.localID
}
if (index != -1) {
requestsForUser.removeAt(index)
}
requestsForUser.add(updated)
dispatchRequestUpdated(updated)
} }
override fun beginKeyVerificationInDMs(method: String, transactionId: String, roomId: String, override fun beginKeyVerificationInDMs(method: String, transactionId: String, roomId: String,
@ -681,6 +753,27 @@ internal class DefaultSasVerificationService @Inject constructor(
} }
} }
override fun readyPendingVerificationInDMs(otherUserId: String, roomId: String, transactionId: String) {
// Let's find the related request
getExistingVerificationRequest(otherUserId)?.find { it.transactionId == transactionId }?.let {
//we need to send a ready event, with matching methods
val transport = sasTransportRoomMessageFactory.createTransport(roomId, cryptoService, null)
val methods = it.requestInfo?.methods?.intersect(listOf(KeyVerificationStart.VERIF_METHOD_SAS))?.toList()
if (methods.isNullOrEmpty()) {
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
return@let
}
//TODO this is not yet related to a transaction, maybe we should use another method like for cancel?
val readyMsg = transport.createReady(transactionId, credentials.deviceId ?: "", methods)
transport.sendToOther(EventType.KEY_VERIFICATION_READY, readyMsg,
SasVerificationTxState.None,
CancelCode.User,
null // TODO handle error?
)
updateOutgoingPendingRequest(it.copy(readyInfo = readyMsg))
}
}
/** /**
* This string must be unique for the pair of users performing verification for the duration that the transaction is valid * This string must be unique for the pair of users performing verification for the duration that the transaction is valid
*/ */

View File

@ -169,6 +169,11 @@ internal abstract class SASVerificationTransaction(
} // if not wait for it } // if not wait for it
} }
override fun shortCodeDoNotMatch() {
Timber.v("## SAS short code do not match for id:$transactionId")
cancel(CancelCode.MismatchedSas)
}
override fun acceptVerificationEvent(senderId: String, info: VerificationInfo) { override fun acceptVerificationEvent(senderId: String, info: VerificationInfo) {
when (info) { when (info) {
is VerificationInfoStart -> onVerificationStart(info) is VerificationInfoStart -> onVerificationStart(info)

View File

@ -58,4 +58,7 @@ internal interface SasTransport {
shortAuthenticationStrings: List<String>) : VerificationInfoStart shortAuthenticationStrings: List<String>) : VerificationInfoStart
fun createMac(tid: String, mac: Map<String, String>, keys: String): VerificationInfoMac fun createMac(tid: String, mac: Map<String, String>, keys: String): VerificationInfoMac
fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady
} }

View File

@ -167,6 +167,17 @@ internal class SasTransportRoomMessage(
) )
) )
} }
override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady {
return MessageVerificationReadyContent(
fromDevice = fromDevice,
relatesTo = RelationDefaultContent(
type = RelationType.REFERENCE,
eventId = tid
),
methods = methods
)
}
} }
internal class SasTransportRoomMessageFactory @Inject constructor( internal class SasTransportRoomMessageFactory @Inject constructor(

View File

@ -126,6 +126,14 @@ internal class SasTransportToDevice(
messageAuthenticationCodes, messageAuthenticationCodes,
shortAuthenticationStrings) shortAuthenticationStrings)
} }
override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady {
return KeyVerificationReady(
transactionID = tid,
fromDevice = fromDevice,
methods = methods
)
}
} }
internal class SasTransportToDeviceFactory @Inject constructor( internal class SasTransportToDeviceFactory @Inject constructor(

View File

@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.crypto.verification
import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceObject import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceObject
internal interface VerificationInfo { interface VerificationInfo {
fun toEventContent(): Content? = null fun toEventContent(): Content? = null
fun toSendToDeviceObject(): SendToDeviceObject? = null fun toSendToDeviceObject(): SendToDeviceObject? = null
fun isValid() : Boolean fun isValid() : Boolean

View File

@ -0,0 +1,42 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto.verification
/**
* A new event type is added to the key verification framework: m.key.verification.ready,
* which may be sent by the target of the m.key.verification.request message, upon receipt of the m.key.verification.request event.
*
* The m.key.verification.ready event is optional; the recipient of the m.key.verification.request event may respond directly
* with a m.key.verification.start event instead.
*/
interface VerificationInfoReady : VerificationInfo {
val transactionID: String?
/**
* The ID of the device that sent the m.key.verification.ready message
*/
val fromDevice: String?
/**
* An array of verification methods that the device supports
*/
val methods: List<String>?
}
internal interface MessageVerificationReadyFactory {
fun create(tid: String, methods: List<String>, fromDevice: String): VerificationInfoReady
}

View File

@ -49,6 +49,7 @@ internal class VerificationMessageLiveObserver @Inject constructor(
EventType.KEY_VERIFICATION_MAC, EventType.KEY_VERIFICATION_MAC,
EventType.KEY_VERIFICATION_CANCEL, EventType.KEY_VERIFICATION_CANCEL,
EventType.KEY_VERIFICATION_DONE, EventType.KEY_VERIFICATION_DONE,
EventType.KEY_VERIFICATION_READY,
EventType.MESSAGE, EventType.MESSAGE,
EventType.ENCRYPTED) EventType.ENCRYPTED)
) )

View File

@ -71,6 +71,20 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
} }
} }
override fun getExistingDirectRoomWithUser(otherUserId: String): Room? {
Realm.getInstance(monarchy.realmConfiguration).use { realm ->
val roomId = RoomSummaryEntity.where(realm)
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
.findAll()?.let { dms ->
dms.firstOrNull {
it.otherMemberIds.contains(otherUserId)
}
}
?.roomId ?: return null
return RoomEntity.where(realm, roomId).findFirst()?.let { roomFactory.create(roomId) }
}
}
override fun getRoomSummary(roomIdOrAlias: String): RoomSummary? { override fun getRoomSummary(roomIdOrAlias: String): RoomSummary? {
return monarchy return monarchy
.fetchCopyMap({ .fetchCopyMap({

View File

@ -21,4 +21,21 @@
<string name="key_verification_request_fallback_message">%s is requesting to verify your key, but your client does not support in-chat key verification. You will need to use legacy key verification to verify keys.</string> <string name="key_verification_request_fallback_message">%s is requesting to verify your key, but your client does not support in-chat key verification. You will need to use legacy key verification to verify keys.</string>
<!-- Sender name of a message when it is send by you, e.g. You: Hello!-->
<string name="you">You</string>
<string name="verify_by_scanning_title">Verify by scanning</string>
<!-- the %s will be replaced by verify_open_camera_link that will be clickable -->
<string name="verify_by_scanning_description">Ask the other user to scan this code, or %s to scan theirs</string>
<!-- This part is inserted in verify_by_scanning_description-->
<string name="verify_open_camera_link">open your camera</string>
<string name="verify_by_emoji_title">Verify by Emoji</string>
<string name="verify_by_emoji_description">If you cant scan the code above, verify by comparing a short, unique selection of emoji.</string>
<string name="aria_qr_code_description">QR code image</string>
<string name="verification_request_alert_title">Verify %s</string>
<string name="verification_request_waiting_for">Waiting for %s…</string>
<string name="verification_request_alert_description">For extra security, verify %s by checking a one-time code on both your devices.\n\nFor maximum security, do this in person.</string>
</resources> </resources>

View File

@ -23,10 +23,7 @@ import dagger.Binds
import dagger.Module import dagger.Module
import dagger.multibindings.IntoMap import dagger.multibindings.IntoMap
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment import im.vector.riotx.features.crypto.verification.*
import im.vector.riotx.features.crypto.verification.SASVerificationShortCodeFragment
import im.vector.riotx.features.crypto.verification.SASVerificationStartFragment
import im.vector.riotx.features.crypto.verification.SASVerificationVerifiedFragment
import im.vector.riotx.features.home.HomeDetailFragment import im.vector.riotx.features.home.HomeDetailFragment
import im.vector.riotx.features.home.HomeDrawerFragment import im.vector.riotx.features.home.HomeDrawerFragment
import im.vector.riotx.features.home.LoadingFragment import im.vector.riotx.features.home.LoadingFragment

View File

@ -25,6 +25,7 @@ import im.vector.riotx.core.error.ErrorFormatter
import im.vector.riotx.core.preference.UserAvatarPreference import im.vector.riotx.core.preference.UserAvatarPreference
import im.vector.riotx.features.MainActivity import im.vector.riotx.features.MainActivity
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
import im.vector.riotx.features.home.HomeActivity import im.vector.riotx.features.home.HomeActivity
import im.vector.riotx.features.home.HomeModule import im.vector.riotx.features.home.HomeModule
import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity
@ -133,6 +134,8 @@ interface ScreenComponent {
fun inject(activity: SoftLogoutActivity) fun inject(activity: SoftLogoutActivity)
fun inject(verificationBottomSheet: VerificationBottomSheet)
fun inject(permalinkHandlerActivity: PermalinkHandlerActivity) fun inject(permalinkHandlerActivity: PermalinkHandlerActivity)
@Component.Factory @Component.Factory

View File

@ -24,7 +24,6 @@ import butterknife.OnClick
import com.airbnb.mvrx.* import com.airbnb.mvrx.*
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.platform.parentFragmentViewModel
import kotlinx.android.synthetic.main.fragment_bottom_sas_verification_code.* import kotlinx.android.synthetic.main.fragment_bottom_sas_verification_code.*
import javax.inject.Inject import javax.inject.Inject

View File

@ -21,10 +21,10 @@ import androidx.core.text.toSpannable
import androidx.core.view.isVisible import androidx.core.view.isVisible
import butterknife.OnClick import butterknife.OnClick
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.platform.parentFragmentViewModel
import im.vector.riotx.core.utils.tappableMatchingText import im.vector.riotx.core.utils.tappableMatchingText
import kotlinx.android.synthetic.main.fragment_verification_choose_method.* import kotlinx.android.synthetic.main.fragment_verification_choose_method.*
import javax.inject.Inject import javax.inject.Inject

View File

@ -19,11 +19,11 @@ import android.os.Parcelable
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import butterknife.OnClick import butterknife.OnClick
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.platform.parentFragmentViewModel
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_verification_conclusion.* import kotlinx.android.synthetic.main.fragment_verification_conclusion.*
@ -56,7 +56,9 @@ class VerificationConclusionFragment @Inject constructor() : VectorBaseFragment(
verifyConclusionDescription.setTextOrHide(null) verifyConclusionDescription.setTextOrHide(null)
verifyConclusionImageView.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_shield_warning)) verifyConclusionImageView.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_shield_warning))
verifyConclusionBottomDescription.text = Markwon.builder(requireContext()).build().toMarkdown(getString(R.string.verification_conclusion_compromised)) verifyConclusionBottomDescription.text = Markwon.builder(requireContext())
.build()
.toMarkdown(getString(R.string.verification_conclusion_compromised))
} }
ConclusionState.CANCELLED -> { ConclusionState.CANCELLED -> {
// Just dismiss in this case // Just dismiss in this case

View File

@ -22,10 +22,10 @@ import androidx.core.view.isVisible
import butterknife.OnClick import butterknife.OnClick
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.platform.parentFragmentViewModel
import im.vector.riotx.core.utils.colorizeMatchingText import im.vector.riotx.core.utils.colorizeMatchingText
import im.vector.riotx.core.utils.styleMatchingText import im.vector.riotx.core.utils.styleMatchingText
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer

View File

@ -66,4 +66,6 @@ sealed class RoomDetailAction : VectorViewModelAction {
data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction() data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction()
data class DeclineVerificationRequest(val transactionId: String) : RoomDetailAction() data class DeclineVerificationRequest(val transactionId: String) : RoomDetailAction()
data class RequestVerification(val userId: String) : RoomDetailAction()
} }

View File

@ -90,6 +90,7 @@ import im.vector.riotx.features.autocomplete.group.AutocompleteGroupPresenter
import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
import im.vector.riotx.features.command.Command import im.vector.riotx.features.command.Command
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.getColorFromUserId import im.vector.riotx.features.home.getColorFromUserId
import im.vector.riotx.features.home.room.detail.composer.TextComposerAction import im.vector.riotx.features.home.room.detail.composer.TextComposerAction

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/bottomSheetScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:fadeScrollbars="false"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="16dp">
<ImageView
android:id="@+id/verificationRequestAvatar"
android:layout_width="32dp"
android:layout_height="32dp"
android:adjustViewBounds="true"
android:background="@drawable/circle"
android:contentDescription="@string/avatar"
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />
<TextView
android:id="@+id/verificationRequestName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:text="@string/verification_request_alert_title"
android:textColor="?riotx_text_primary"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
<FrameLayout
android:id="@+id/bottomSheetFragmentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- <ImageView-->
<!-- android:id="@+id/verificationRequestAvatar"-->
<!-- android:layout_width="32dp"-->
<!-- android:layout_height="32dp"-->
<!-- android:adjustViewBounds="true"-->
<!-- android:background="@drawable/circle"-->
<!-- android:contentDescription="@string/avatar"-->
<!-- android:scaleType="centerCrop"-->
<!-- android:transitionName="bottomSheetAvatar"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:layout_constraintVertical_bias="0"-->
<!-- tools:src="@tools:sample/avatars" />-->
<!-- <TextView-->
<!-- android:id="@+id/verificationRequestName"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="16dp"-->
<!-- android:text="@string/verification_request_alert_title"-->
<!-- android:textColor="?riotx_text_primary"-->
<!-- android:textSize="20sp"-->
<!-- android:textStyle="bold"-->
<!-- android:transitionName="bottomSheetDisplayName"-->
<!-- app:layout_constraintBottom_toBottomOf="@id/verificationRequestAvatar"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toEndOf="@id/verificationRequestAvatar"-->
<!-- app:layout_constraintTop_toTopOf="@id/verificationRequestAvatar" />-->
<TextView
android:id="@+id/verificationRequestText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:textColor="?riotx_text_secondary"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/verification_request_alert_description" />
<!-- app:layout_constraintTop_toBottomOf="@id/verificationRequestAvatar"-->
<com.google.android.material.button.MaterialButton
android:id="@+id/verificationStartButton"
style="@style/VectorButtonStylePositive"
android:layout_width="match_parent"
android:layout_marginTop="16dp"
android:text="@string/start_verification"
app:layout_constraintTop_toBottomOf="@id/verificationRequestText"
tools:visibility="visible" />
<TextView
android:id="@+id/verificationWaitingText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textColor="?vctr_notice_secondary"
android:textSize="17sp"
android:textStyle="bold"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@id/verificationStartButton"
app:layout_constraintTop_toTopOf="@id/verificationStartButton"
tools:text="@string/verification_request_waiting_for" />
</androidx.constraintlayout.widget.ConstraintLayout>