mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
merge madness ??
This commit is contained in:
parent
3eed9b5083
commit
3c4506cb58
28 changed files with 453 additions and 14 deletions
|
@ -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) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,4 +47,7 @@ interface SasVerificationTransaction {
|
|||
* both short codes do match
|
||||
*/
|
||||
fun userHasVerifiedShortCode()
|
||||
|
||||
|
||||
fun shortCodeDoNotMatch()
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -89,4 +89,6 @@ interface RoomService {
|
|||
fun getRoomIdByAlias(roomAlias: String,
|
||||
searchOnServer: Boolean,
|
||||
callback: MatrixCallback<Optional<String>>): Cancelable
|
||||
|
||||
fun getExistingDirectRoomWithUser(otherUserId: String) : Room?
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
)
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 can’t 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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
55
vector/src/main/res/layout/bottom_sheet_verification.xml
Normal file
55
vector/src/main/res/layout/bottom_sheet_verification.xml
Normal 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>
|
73
vector/src/main/res/layout/fragment_verification_request.xml
Normal file
73
vector/src/main/res/layout/fragment_verification_request.xml
Normal 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>
|
Loading…
Reference in a new issue