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
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
@ -54,7 +55,7 @@ interface SasVerificationService {
*/
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,
transactionId: String,
@ -63,11 +64,16 @@ interface SasVerificationService {
otherDeviceId: String,
callback: MatrixCallback<String>?): String?
fun readyPendingVerificationInDMs(otherUserId: String, roomId: String, transactionId: String)
// fun transactionUpdated(tx: SasVerificationTransaction)
interface SasVerificationListener {
fun transactionCreated(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
*/
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_CANCEL = "m.key.verification.cancel"
const val KEY_VERIFICATION_DONE = "m.key.verification.done"
const val KEY_VERIFICATION_READY = "m.key.verification.ready"
// Relation Events
const val REACTION = "m.reaction"

View File

@ -89,4 +89,6 @@ interface RoomService {
fun getRoomIdByAlias(roomAlias: String,
searchOnServer: Boolean,
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 {
const val VERIF_METHOD_SAS = "m.sas.v1"
const val VERIF_METHOD_SCAN = "m.qr_code.scan.v1"
}
override fun isValid(): Boolean {

View File

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

View File

@ -75,6 +75,12 @@ internal class DefaultSasVerificationService @Inject constructor(
// map [sender : [transaction]]
private val txMap = HashMap<String, HashMap<String, VerificationTransaction>>()
/**
* Map [sender: [PendingVerificationRequest]]
*/
private val pendingRequests = HashMap<String, ArrayList<PendingVerificationRequest>>()
// Event received from the sync
fun onToDeviceEvent(event: Event) {
GlobalScope.launch(coroutineDispatchers.crypto) {
@ -120,6 +126,9 @@ internal class DefaultSasVerificationService @Inject constructor(
EventType.KEY_VERIFICATION_MAC -> {
onRoomMacReceived(event)
}
EventType.KEY_VERIFICATION_READY -> {
onRoomReadyReceived(event)
}
EventType.KEY_VERIFICATION_DONE -> {
// 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) {
setDeviceVerificationAction.handle(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED,
deviceID,
@ -326,6 +360,10 @@ internal class DefaultSasVerificationService @Inject constructor(
// Ok we can create
if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) {
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(
// this,
setDeviceVerificationAction,
@ -333,7 +371,8 @@ internal class DefaultSasVerificationService @Inject constructor(
cryptoStore,
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
startReq.transactionID!!,
otherUserId).also { txConfigure(it) }
otherUserId,
autoAccept).also { txConfigure(it) }
addTransaction(tx)
tx.acceptVerificationEvent(otherUserId, startReq)
} 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? {
synchronized(lock = txMap) {
return txMap[otherUser]?.get(tid)
@ -647,6 +695,12 @@ internal class DefaultSasVerificationService @Inject constructor(
) {
this.callback = object : MatrixCallback<SendResponse> {
override fun onSuccess(data: SendResponse) {
params.event.getClearContent().toModel<MessageVerificationRequestContent>()?.let {
updateOutgoingPendingRequest(verificationRequest.copy(
transactionId = data.eventId,
requestInfo = it
))
}
callback?.onSuccess(data.eventId)
}
@ -657,6 +711,24 @@ internal class DefaultSasVerificationService @Inject constructor(
constraints = TaskConstraints(true)
retryCount = 3
}.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,
@ -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
*/

View File

@ -169,6 +169,11 @@ internal abstract class SASVerificationTransaction(
} // 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) {
when (info) {
is VerificationInfoStart -> onVerificationStart(info)

View File

@ -58,4 +58,7 @@ internal interface SasTransport {
shortAuthenticationStrings: List<String>) : VerificationInfoStart
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(

View File

@ -126,6 +126,14 @@ internal class SasTransportToDevice(
messageAuthenticationCodes,
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(

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.internal.crypto.model.rest.SendToDeviceObject
internal interface VerificationInfo {
interface VerificationInfo {
fun toEventContent(): Content? = null
fun toSendToDeviceObject(): SendToDeviceObject? = null
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_CANCEL,
EventType.KEY_VERIFICATION_DONE,
EventType.KEY_VERIFICATION_READY,
EventType.MESSAGE,
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? {
return monarchy
.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>
<!-- 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>

View File

@ -23,10 +23,7 @@ import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment
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.crypto.verification.*
import im.vector.riotx.features.home.HomeDetailFragment
import im.vector.riotx.features.home.HomeDrawerFragment
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.features.MainActivity
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.HomeModule
import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity
@ -133,6 +134,8 @@ interface ScreenComponent {
fun inject(activity: SoftLogoutActivity)
fun inject(verificationBottomSheet: VerificationBottomSheet)
fun inject(permalinkHandlerActivity: PermalinkHandlerActivity)
@Component.Factory

View File

@ -24,7 +24,6 @@ import butterknife.OnClick
import com.airbnb.mvrx.*
import im.vector.riotx.R
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 javax.inject.Inject

View File

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

View File

@ -19,11 +19,11 @@ import android.os.Parcelable
import androidx.core.content.ContextCompat
import butterknife.OnClick
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotx.R
import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.platform.parentFragmentViewModel
import io.noties.markwon.Markwon
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_verification_conclusion.*
@ -56,7 +56,9 @@ class VerificationConclusionFragment @Inject constructor() : VectorBaseFragment(
verifyConclusionDescription.setTextOrHide(null)
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 -> {
// Just dismiss in this case

View File

@ -22,10 +22,10 @@ import androidx.core.view.isVisible
import butterknife.OnClick
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotx.R
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.styleMatchingText
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 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.user.AutocompleteUserPresenter
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.getColorFromUserId
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>