mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +03:00
Merge pull request #811 from vector-im/verification_toasters
[Cross Signing DM Verif] Basic Incoming request toast + cleaning
This commit is contained in:
commit
ea9166e0c6
100 changed files with 2071 additions and 2609 deletions
|
@ -1,5 +1,6 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
|
@ -44,22 +44,18 @@ interface SasVerificationService {
|
|||
|
||||
fun getExistingVerificationRequest(otherUser: String, tid: String?): PendingVerificationRequest?
|
||||
|
||||
/**
|
||||
* Shortcut for KeyVerificationStart.VERIF_METHOD_SAS
|
||||
* @see beginKeyVerification
|
||||
*/
|
||||
fun beginKeyVerificationSAS(userId: String, deviceID: String): String?
|
||||
fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest?
|
||||
|
||||
fun beginKeyVerification(method: VerificationMethod, userId: String, deviceID: String): String?
|
||||
|
||||
/**
|
||||
* Request a key verification from another user using toDevice events.
|
||||
*/
|
||||
fun beginKeyVerification(method: String, userId: String, deviceID: String): String?
|
||||
|
||||
fun requestKeyVerificationInDMs(userId: String, roomId: String): PendingVerificationRequest
|
||||
fun requestKeyVerificationInDMs(methods: List<VerificationMethod>, userId: String, roomId: String): PendingVerificationRequest
|
||||
|
||||
fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String)
|
||||
|
||||
fun beginKeyVerificationInDMs(method: String,
|
||||
fun beginKeyVerificationInDMs(method: VerificationMethod,
|
||||
transactionId: String,
|
||||
roomId: String,
|
||||
otherUserId: String,
|
||||
|
@ -67,7 +63,7 @@ interface SasVerificationService {
|
|||
callback: MatrixCallback<String>?): String?
|
||||
|
||||
/**
|
||||
* Returns false if the request is unknwown
|
||||
* Returns false if the request is unknown
|
||||
*/
|
||||
fun readyPendingVerificationInDMs(otherUserId: String, roomId: String, transactionId: String): Boolean
|
||||
|
||||
|
@ -84,14 +80,14 @@ interface SasVerificationService {
|
|||
|
||||
companion object {
|
||||
|
||||
private const val TEN_MINTUTES_IN_MILLIS = 10 * 60 * 1000
|
||||
private const val FIVE_MINTUTES_IN_MILLIS = 5 * 60 * 1000
|
||||
private const val TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000
|
||||
private const val FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000
|
||||
|
||||
fun isValidRequest(age: Long?): Boolean {
|
||||
if (age == null) return false
|
||||
val now = System.currentTimeMillis()
|
||||
val tooInThePast = now - TEN_MINTUTES_IN_MILLIS
|
||||
val tooInTheFuture = now + FIVE_MINTUTES_IN_MILLIS
|
||||
val tooInThePast = now - TEN_MINUTES_IN_MILLIS
|
||||
val tooInTheFuture = now + FIVE_MINUTES_IN_MILLIS
|
||||
return age in tooInThePast..tooInTheFuture
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.api.session.crypto.sas
|
||||
|
||||
/**
|
||||
* Verification methods supported (or to be supported) by the matrix SDK
|
||||
*/
|
||||
enum class VerificationMethod {
|
||||
SAS,
|
||||
// Not supported yet
|
||||
SCAN
|
||||
}
|
|
@ -20,7 +20,7 @@ import com.squareup.moshi.JsonClass
|
|||
import im.vector.matrix.android.api.session.crypto.sas.SasMode
|
||||
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.model.rest.KeyVerificationStart
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.supportedVerificationMethods
|
||||
import im.vector.matrix.android.internal.crypto.verification.SASVerificationTransaction
|
||||
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart
|
||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||
|
@ -45,12 +45,11 @@ internal data class MessageVerificationStartContent(
|
|||
get() = relatesTo?.eventId
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
if (
|
||||
(transactionID.isNullOrBlank()
|
||||
|| fromDevice.isNullOrBlank()
|
||||
|| method != KeyVerificationStart.VERIF_METHOD_SAS
|
||||
|| keyAgreementProtocols.isNullOrEmpty()
|
||||
|| hashes.isNullOrEmpty())
|
||||
if (transactionID.isNullOrBlank()
|
||||
|| fromDevice.isNullOrBlank()
|
||||
|| method !in supportedVerificationMethods
|
||||
|| keyAgreementProtocols.isNullOrEmpty()
|
||||
|| hashes.isNullOrEmpty()
|
||||
|| !hashes.contains("sha256") || messageAuthenticationCodes.isNullOrEmpty()
|
||||
|| (!messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256)
|
||||
&& !messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|
||||
|
|
|
@ -25,8 +25,7 @@ import im.vector.matrix.android.internal.crypto.verification.VerificationInfoRea
|
|||
@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 = "methods") override val methods: List<String>?,
|
||||
@Json(name = "transaction_id") override var transactionID: String? = null
|
||||
) : SendToDeviceObject, VerificationInfoReady {
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ internal data class KeyVerificationRequest(
|
|||
@Json(name = "from_device")
|
||||
val fromDevice: String,
|
||||
/** The verification methods supported by the sender. */
|
||||
val methods: List<String> = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
|
||||
val methods: List<String>,
|
||||
/**
|
||||
* The POSIX timestamp in milliseconds for when the request was made.
|
||||
* If the request is in the future by more than 5 minutes or more than 10 minutes in the past,
|
||||
|
|
|
@ -27,7 +27,7 @@ import timber.log.Timber
|
|||
* Sent by Alice to initiate an interactive key verification.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class KeyVerificationStart(
|
||||
internal data class KeyVerificationStart(
|
||||
@Json(name = "from_device") override val fromDevice: String? = null,
|
||||
override val method: String? = null,
|
||||
@Json(name = "transaction_id") override val transactionID: String? = null,
|
||||
|
@ -41,22 +41,18 @@ data class KeyVerificationStart(
|
|||
return JsonCanonicalizer.getCanonicalJson(KeyVerificationStart::class.java, this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VERIF_METHOD_SAS = "m.sas.v1"
|
||||
const val VERIF_METHOD_SCAN = "m.qr_code.scan.v1"
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
if ((transactionID.isNullOrBlank()
|
||||
|| fromDevice.isNullOrBlank()
|
||||
|| method != VERIF_METHOD_SAS
|
||||
|| keyAgreementProtocols.isNullOrEmpty()
|
||||
|| hashes.isNullOrEmpty())
|
||||
if (transactionID.isNullOrBlank()
|
||||
|| fromDevice.isNullOrBlank()
|
||||
|| method !in supportedVerificationMethods
|
||||
|| keyAgreementProtocols.isNullOrEmpty()
|
||||
|| hashes.isNullOrEmpty()
|
||||
|| !hashes.contains("sha256")
|
||||
|| messageAuthenticationCodes.isNullOrEmpty()
|
||||
|| (!messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256)
|
||||
&& !messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|
||||
|| shortAuthenticationStrings.isNullOrEmpty() || !shortAuthenticationStrings.contains(SasMode.DECIMAL)) {
|
||||
|| shortAuthenticationStrings.isNullOrEmpty()
|
||||
|| !shortAuthenticationStrings.contains(SasMode.DECIMAL)) {
|
||||
Timber.e("## received invalid verification request")
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.model.rest
|
||||
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
||||
|
||||
internal const val VERIFICATION_METHOD_SAS = "m.sas.v1"
|
||||
internal const val VERIFICATION_METHOD_SCAN = "m.qr_code.scan.v1"
|
||||
|
||||
internal fun VerificationMethod.toValue(): String {
|
||||
return when (this) {
|
||||
VerificationMethod.SAS -> VERIFICATION_METHOD_SAS
|
||||
VerificationMethod.SCAN -> VERIFICATION_METHOD_SCAN
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Add SCAN
|
||||
internal val supportedVerificationMethods = listOf(VERIFICATION_METHOD_SAS)
|
|
@ -104,6 +104,7 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
|
|||
// The verification is started from another device
|
||||
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
|
||||
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
||||
params.sasVerificationService.onRoomRequestHandledByOtherDevice(event)
|
||||
}
|
||||
}
|
||||
} else if (EventType.KEY_VERIFICATION_READY == event.type) {
|
||||
|
@ -112,11 +113,13 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
|
|||
// The verification is started from another device
|
||||
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
|
||||
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
||||
params.sasVerificationService.onRoomRequestHandledByOtherDevice(event)
|
||||
}
|
||||
}
|
||||
} else if (EventType.KEY_VERIFICATION_CANCEL == event.type || EventType.KEY_VERIFICATION_DONE == event.type) {
|
||||
event.getClearContent().toModel<MessageRelationContent>()?.relatesTo?.eventId?.let {
|
||||
transactionsHandledByOtherDevice.remove(it)
|
||||
params.sasVerificationService.onRoomRequestHandledByOtherDevice(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,10 @@ import im.vector.matrix.android.api.auth.data.Credentials
|
|||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.toValue
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -71,7 +72,7 @@ internal class DefaultOutgoingSASVerificationRequest(
|
|||
cancel(CancelCode.UnexpectedMessage)
|
||||
}
|
||||
|
||||
fun start() {
|
||||
fun start(method: VerificationMethod) {
|
||||
if (state != SasVerificationTxState.None) {
|
||||
Timber.e("## SAS O: start verification from invalid state")
|
||||
// should I cancel??
|
||||
|
@ -80,7 +81,7 @@ internal class DefaultOutgoingSASVerificationRequest(
|
|||
|
||||
val startMessage = transport.createStart(
|
||||
credentials.deviceId ?: "",
|
||||
KeyVerificationStart.VERIF_METHOD_SAS,
|
||||
method.toValue(),
|
||||
transactionId,
|
||||
KNOWN_AGREEMENT_PROTOCOLS,
|
||||
KNOWN_HASHES,
|
||||
|
|
|
@ -22,10 +22,7 @@ import dagger.Lazy
|
|||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.api.session.crypto.sas.safeValueOf
|
||||
import im.vector.matrix.android.api.session.crypto.sas.*
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
||||
|
@ -216,7 +213,20 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onRoomRequestReceived(event: Event) {
|
||||
fun onRoomRequestHandledByOtherDevice(event: Event) {
|
||||
val requestInfo = event.getClearContent().toModel<MessageRelationContent>()
|
||||
?: return
|
||||
val requestId = requestInfo.relatesTo?.eventId ?: return
|
||||
getExistingVerificationRequestInRoom(event.roomId ?: "", requestId)?.let {
|
||||
updatePendingRequest(
|
||||
it.copy(
|
||||
handledByOtherSession = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun onRoomRequestReceived(event: Event) {
|
||||
Timber.v("## SAS Verification request from ${event.senderId} in room ${event.roomId}")
|
||||
val requestInfo = event.getClearContent().toModel<MessageVerificationRequestContent>()
|
||||
?: return
|
||||
|
@ -245,6 +255,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
ageLocalTs = event.ageLocalTs ?: System.currentTimeMillis(),
|
||||
isIncoming = true,
|
||||
otherUserId = senderId, // requestInfo.toUserId,
|
||||
roomId = event.roomId,
|
||||
transactionId = event.eventId,
|
||||
requestInfo = requestInfo
|
||||
)
|
||||
|
@ -274,31 +285,27 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
if (startReq?.isValid()?.not() == true) {
|
||||
Timber.e("## received invalid verification request")
|
||||
if (startReq.transactionID != null) {
|
||||
sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
||||
?: "", event.roomId
|
||||
?: "", null).cancelTransaction(
|
||||
startReq.transactionID ?: "",
|
||||
otherUserId!!,
|
||||
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||
CancelCode.UnknownMethod
|
||||
)
|
||||
sasTransportRoomMessageFactory.createTransport(event.roomId ?: "", null)
|
||||
.cancelTransaction(
|
||||
startReq.transactionID ?: "",
|
||||
otherUserId!!,
|
||||
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||
CancelCode.UnknownMethod
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
handleStart(otherUserId, startReq as VerificationInfoStart) {
|
||||
it.transport = sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
||||
?: "", event.roomId
|
||||
?: "", it)
|
||||
it.transport = sasTransportRoomMessageFactory.createTransport(event.roomId ?: "", it)
|
||||
}?.let {
|
||||
sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
||||
?: "", event.roomId
|
||||
?: "", null).cancelTransaction(
|
||||
startReq.transactionID ?: "",
|
||||
otherUserId!!,
|
||||
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||
it
|
||||
)
|
||||
sasTransportRoomMessageFactory.createTransport(event.roomId ?: "", null)
|
||||
.cancelTransaction(
|
||||
startReq.transactionID ?: "",
|
||||
otherUserId!!,
|
||||
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,7 +363,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
// cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||
} else {
|
||||
// Ok we can create
|
||||
if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) {
|
||||
if (startReq.method == VERIFICATION_METHOD_SAS) {
|
||||
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)
|
||||
|
@ -647,6 +654,16 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest? {
|
||||
synchronized(lock = pendingRequests) {
|
||||
return tid?.let { tid ->
|
||||
pendingRequests.flatMap { entry ->
|
||||
entry.value.filter { it.roomId == roomId && it.transactionId == tid }
|
||||
}.firstOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExistingTransactionsForUser(otherUser: String): Collection<VerificationTransaction>? {
|
||||
synchronized(txMap) {
|
||||
return txMap[otherUser]?.values
|
||||
|
@ -670,14 +687,10 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun beginKeyVerificationSAS(userId: String, deviceID: String): String? {
|
||||
return beginKeyVerification(KeyVerificationStart.VERIF_METHOD_SAS, userId, deviceID)
|
||||
}
|
||||
|
||||
override fun beginKeyVerification(method: String, userId: String, deviceID: String): String? {
|
||||
override fun beginKeyVerification(method: VerificationMethod, userId: String, deviceID: String): String? {
|
||||
val txID = createUniqueIDForTransaction(userId, deviceID)
|
||||
// should check if already one (and cancel it)
|
||||
if (KeyVerificationStart.VERIF_METHOD_SAS == method) {
|
||||
if (method == VerificationMethod.SAS) {
|
||||
val tx = DefaultOutgoingSASVerificationRequest(
|
||||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
|
@ -689,14 +702,14 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
tx.transport = sasTransportToDeviceFactory.createTransport(tx)
|
||||
addTransaction(tx)
|
||||
|
||||
tx.start()
|
||||
tx.start(method)
|
||||
return txID
|
||||
} else {
|
||||
throw IllegalArgumentException("Unknown verification method")
|
||||
}
|
||||
}
|
||||
|
||||
override fun requestKeyVerificationInDMs(userId: String, roomId: String)
|
||||
override fun requestKeyVerificationInDMs(methods: List<VerificationMethod>, userId: String, roomId: String)
|
||||
: PendingVerificationRequest {
|
||||
Timber.i("## SAS Requesting verification to user: $userId in room $roomId")
|
||||
|
||||
|
@ -705,8 +718,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
pendingRequests[userId] = it
|
||||
}
|
||||
|
||||
val transport = sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
||||
?: "", roomId, null)
|
||||
val transport = sasTransportRoomMessageFactory.createTransport(roomId, null)
|
||||
|
||||
// Cancel existing pending requests?
|
||||
requestsForUser.forEach { existingRequest ->
|
||||
|
@ -723,11 +735,12 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
val verificationRequest = PendingVerificationRequest(
|
||||
ageLocalTs = System.currentTimeMillis(),
|
||||
isIncoming = false,
|
||||
roomId = roomId,
|
||||
localID = localID,
|
||||
otherUserId = userId
|
||||
)
|
||||
|
||||
transport.sendVerificationRequest(localID, userId, roomId) { syncedId, info ->
|
||||
transport.sendVerificationRequest(methods.map { it.toValue() }, localID, userId, roomId) { syncedId, info ->
|
||||
// We need to update with the syncedID
|
||||
updatePendingRequest(verificationRequest.copy(
|
||||
transactionId = syncedId,
|
||||
|
@ -742,8 +755,8 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
}
|
||||
|
||||
override fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String) {
|
||||
sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
||||
?: "", roomId, null).cancelTransaction(transactionId, otherUserId, otherDeviceId, CancelCode.User)
|
||||
sasTransportRoomMessageFactory.createTransport(roomId, null)
|
||||
.cancelTransaction(transactionId, otherUserId, otherDeviceId, CancelCode.User)
|
||||
|
||||
getExistingVerificationRequest(otherUserId, transactionId)?.let {
|
||||
updatePendingRequest(it.copy(
|
||||
|
@ -768,10 +781,13 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
dispatchRequestUpdated(updated)
|
||||
}
|
||||
|
||||
override fun beginKeyVerificationInDMs(method: String, transactionId: String, roomId: String,
|
||||
otherUserId: String, otherDeviceId: String,
|
||||
override fun beginKeyVerificationInDMs(method: VerificationMethod,
|
||||
transactionId: String,
|
||||
roomId: String,
|
||||
otherUserId: String,
|
||||
otherDeviceId: String,
|
||||
callback: MatrixCallback<String>?): String? {
|
||||
if (KeyVerificationStart.VERIF_METHOD_SAS == method) {
|
||||
if (method == VerificationMethod.SAS) {
|
||||
val tx = DefaultOutgoingSASVerificationRequest(
|
||||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
|
@ -780,11 +796,10 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
transactionId,
|
||||
otherUserId,
|
||||
otherDeviceId)
|
||||
tx.transport = sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
||||
?: "", roomId, tx)
|
||||
tx.transport = sasTransportRoomMessageFactory.createTransport(roomId, tx)
|
||||
addTransaction(tx)
|
||||
|
||||
tx.start()
|
||||
tx.start(method)
|
||||
return transactionId
|
||||
} else {
|
||||
throw IllegalArgumentException("Unknown verification method")
|
||||
|
@ -797,9 +812,8 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
val existingRequest = getExistingVerificationRequest(otherUserId, transactionId)
|
||||
if (existingRequest != null) {
|
||||
// we need to send a ready event, with matching methods
|
||||
val transport = sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
||||
?: "", roomId, null)
|
||||
val methods = existingRequest.requestInfo?.methods?.intersect(listOf(KeyVerificationStart.VERIF_METHOD_SAS))?.toList()
|
||||
val transport = sasTransportRoomMessageFactory.createTransport(roomId, null)
|
||||
val methods = existingRequest.requestInfo?.methods?.intersect(supportedVerificationMethods)?.toList()
|
||||
if (methods.isNullOrEmpty()) {
|
||||
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
|
||||
// TODO buttons should not be shown in this case?
|
||||
|
|
|
@ -16,27 +16,38 @@
|
|||
package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SCAN
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Stores current pending verification requests
|
||||
*/
|
||||
data class PendingVerificationRequest(
|
||||
val ageLocalTs : Long,
|
||||
val ageLocalTs: Long,
|
||||
val isIncoming: Boolean = false,
|
||||
val localID: String = UUID.randomUUID().toString(),
|
||||
val localID: String = UUID.randomUUID().toString(),
|
||||
val otherUserId: String,
|
||||
val roomId: String?,
|
||||
val transactionId: String? = null,
|
||||
val requestInfo: MessageVerificationRequestContent? = null,
|
||||
val readyInfo: VerificationInfoReady? = null,
|
||||
val cancelConclusion: CancelCode? = null,
|
||||
val isSuccessful : Boolean = false
|
||||
val isSuccessful: Boolean = false,
|
||||
val handledByOtherSession: Boolean = false
|
||||
|
||||
) {
|
||||
|
||||
val isReady: Boolean = readyInfo != null
|
||||
val isSent: Boolean = transactionId != null
|
||||
|
||||
val isFinished: Boolean = isSuccessful || cancelConclusion != null
|
||||
|
||||
fun hasMethod(method: VerificationMethod): Boolean? {
|
||||
return when (method) {
|
||||
VerificationMethod.SAS -> readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS)
|
||||
VerificationMethod.SCAN -> readyInfo?.methods?.contains(VERIFICATION_METHOD_SCAN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,9 +34,16 @@ internal interface SasTransport {
|
|||
onErrorReason: CancelCode,
|
||||
onDone: (() -> Unit)?)
|
||||
|
||||
fun sendVerificationRequest(localID: String, otherUserId: String, roomId: String, callback: (String?, MessageVerificationRequestContent?) -> Unit)
|
||||
fun sendVerificationRequest(supportedMethods: List<String>,
|
||||
localID: String,
|
||||
otherUserId: String,
|
||||
roomId: String, callback:
|
||||
(String?, MessageVerificationRequestContent?) -> Unit)
|
||||
|
||||
fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDevice: String, code: CancelCode)
|
||||
fun cancelTransaction(transactionId: String,
|
||||
otherUserId: String,
|
||||
otherUserDeviceId: String,
|
||||
code: CancelCode)
|
||||
|
||||
fun done(transactionId: String)
|
||||
/**
|
||||
|
@ -58,7 +65,7 @@ internal interface SasTransport {
|
|||
keyAgreementProtocols: List<String>,
|
||||
hashes: List<String>,
|
||||
messageAuthenticationCodes: List<String>,
|
||||
shortAuthenticationStrings: List<String>) : VerificationInfoStart
|
||||
shortAuthenticationStrings: List<String>): VerificationInfoStart
|
||||
|
||||
fun createMac(tid: String, mac: Map<String, String>, keys: String): VerificationInfoMac
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.work.*
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
|
@ -25,9 +24,12 @@ import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
|||
import im.vector.matrix.android.api.session.events.model.*
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
|
||||
import im.vector.matrix.android.internal.di.DeviceId
|
||||
import im.vector.matrix.android.internal.di.SessionId
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.util.StringProvider
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
@ -38,9 +40,11 @@ import java.util.concurrent.TimeUnit
|
|||
import javax.inject.Inject
|
||||
|
||||
internal class SasTransportRoomMessage(
|
||||
private val context: Context,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
private val stringProvider: StringProvider,
|
||||
private val sessionId: String,
|
||||
private val userId: String,
|
||||
private val userDevice: String,
|
||||
private val userDeviceId: String?,
|
||||
private val roomId: String,
|
||||
private val monarchy: Monarchy,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||
|
@ -61,7 +65,7 @@ internal class SasTransportRoomMessage(
|
|||
)
|
||||
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
userId = userId,
|
||||
sessionId = sessionId,
|
||||
event = event
|
||||
))
|
||||
val enqueueInfo = enqueueSendWork(workerParams)
|
||||
|
@ -84,7 +88,8 @@ internal class SasTransportRoomMessage(
|
|||
// }
|
||||
// }, listenerExecutor)
|
||||
|
||||
val workLiveData = WorkManager.getInstance(context).getWorkInfosForUniqueWorkLiveData("${roomId}_VerificationWork")
|
||||
val workLiveData = workManagerProvider.workManager
|
||||
.getWorkInfosForUniqueWorkLiveData("${roomId}_VerificationWork")
|
||||
|
||||
val observer = object : Observer<List<WorkInfo>> {
|
||||
override fun onChanged(workInfoList: List<WorkInfo>?) {
|
||||
|
@ -113,13 +118,16 @@ internal class SasTransportRoomMessage(
|
|||
}
|
||||
}
|
||||
|
||||
override fun sendVerificationRequest(localID: String, otherUserId: String, roomId: String,
|
||||
override fun sendVerificationRequest(supportedMethods: List<String>,
|
||||
localID: String,
|
||||
otherUserId: String,
|
||||
roomId: String,
|
||||
callback: (String?, MessageVerificationRequestContent?) -> Unit) {
|
||||
val info = MessageVerificationRequestContent(
|
||||
body = context.getString(R.string.key_verification_request_fallback_message, userId),
|
||||
fromDevice = userDevice,
|
||||
body = stringProvider.getString(R.string.key_verification_request_fallback_message, userId),
|
||||
fromDevice = userDeviceId ?: "",
|
||||
toUserId = otherUserId,
|
||||
methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS)
|
||||
methods = supportedMethods
|
||||
)
|
||||
val content = info.toContent()
|
||||
|
||||
|
@ -131,24 +139,25 @@ internal class SasTransportRoomMessage(
|
|||
)
|
||||
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
userId = userId,
|
||||
sessionId = sessionId,
|
||||
event = event
|
||||
))
|
||||
|
||||
val workRequest = WorkManagerUtil.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.setInputData(workerParams)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context)
|
||||
workManagerProvider.workManager
|
||||
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
|
||||
.enqueue()
|
||||
|
||||
// I cannot just listen to the given work request, because when used in a uniqueWork,
|
||||
// The callback is called while it is still Running ...
|
||||
|
||||
val workLiveData = WorkManager.getInstance(context).getWorkInfosForUniqueWorkLiveData("${roomId}_VerificationWork")
|
||||
val workLiveData = workManagerProvider.workManager
|
||||
.getWorkInfosForUniqueWorkLiveData("${roomId}_VerificationWork")
|
||||
|
||||
val observer = object : Observer<List<WorkInfo>> {
|
||||
override fun onChanged(workInfoList: List<WorkInfo>?) {
|
||||
|
@ -174,7 +183,7 @@ internal class SasTransportRoomMessage(
|
|||
}
|
||||
}
|
||||
|
||||
override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDevice: String, code: CancelCode) {
|
||||
override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceId: String, code: CancelCode) {
|
||||
Timber.d("## SAS canceling transaction $transactionId for reason $code")
|
||||
val event = createEventAndLocalEcho(
|
||||
type = EventType.KEY_VERIFICATION_CANCEL,
|
||||
|
@ -182,7 +191,7 @@ internal class SasTransportRoomMessage(
|
|||
content = MessageVerificationCancelContent.create(transactionId, code).toContent()
|
||||
)
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
userId = this.userId,
|
||||
sessionId = sessionId,
|
||||
event = event
|
||||
))
|
||||
enqueueSendWork(workerParams)
|
||||
|
@ -200,19 +209,19 @@ internal class SasTransportRoomMessage(
|
|||
).toContent()
|
||||
)
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
userId = userId,
|
||||
sessionId = sessionId,
|
||||
event = event
|
||||
))
|
||||
enqueueSendWork(workerParams)
|
||||
}
|
||||
|
||||
private fun enqueueSendWork(workerParams: Data): Pair<Operation, UUID> {
|
||||
val workRequest = WorkManagerUtil.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.setInputData(workerParams)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
return WorkManager.getInstance(context)
|
||||
return workManagerProvider.workManager
|
||||
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
|
||||
.enqueue() to workRequest.id
|
||||
}
|
||||
|
@ -284,12 +293,18 @@ internal class SasTransportRoomMessage(
|
|||
}
|
||||
|
||||
internal class SasTransportRoomMessageFactory @Inject constructor(
|
||||
private val context: Context,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
private val stringProvider: StringProvider,
|
||||
private val monarchy: Monarchy,
|
||||
@SessionId
|
||||
private val sessionId: String,
|
||||
@UserId
|
||||
private val userId: String,
|
||||
@DeviceId
|
||||
private val deviceId: String?,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory) {
|
||||
|
||||
fun createTransport(userId: String, userDevice: String, roomId: String, tx: SASVerificationTransaction?
|
||||
): SasTransportRoomMessage {
|
||||
return SasTransportRoomMessage(context, userId, userDevice, roomId, monarchy, localEchoEventFactory, tx)
|
||||
fun createTransport(roomId: String, tx: SASVerificationTransaction?): SasTransportRoomMessage {
|
||||
return SasTransportRoomMessage(workManagerProvider, stringProvider, sessionId, userId, deviceId, roomId, monarchy, localEchoEventFactory, tx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,10 @@ internal class SasTransportToDevice(
|
|||
private var taskExecutor: TaskExecutor
|
||||
) : SasTransport {
|
||||
|
||||
override fun sendVerificationRequest(localID: String, otherUserId: String, roomId: String,
|
||||
override fun sendVerificationRequest(supportedMethods: List<String>,
|
||||
localID: String,
|
||||
otherUserId: String,
|
||||
roomId: String,
|
||||
callback: (String?, MessageVerificationRequestContent?) -> Unit) {
|
||||
// TODO "not implemented"
|
||||
}
|
||||
|
@ -78,11 +81,11 @@ internal class SasTransportToDevice(
|
|||
// To device do not do anything here
|
||||
}
|
||||
|
||||
override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDevice: String, code: CancelCode) {
|
||||
override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceId: String, code: CancelCode) {
|
||||
Timber.d("## SAS canceling transaction $transactionId for reason $code")
|
||||
val cancelMessage = KeyVerificationCancel.create(transactionId, code)
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
contentMap.setObject(otherUserId, otherUserDevice, cancelMessage)
|
||||
contentMap.setObject(otherUserId, otherUserDeviceId, cancelMessage)
|
||||
sendToDeviceTask
|
||||
.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) {
|
||||
this.callback = object : MatrixCallback<Unit> {
|
||||
|
|
|
@ -24,6 +24,7 @@ import im.vector.matrix.android.api.failure.shouldBeRetried
|
|||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendVerificationMessageTask
|
||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||
import timber.log.Timber
|
||||
|
@ -34,9 +35,10 @@ internal class SendVerificationMessageWorker constructor(context: Context, param
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class Params(
|
||||
val userId: String,
|
||||
val event: Event
|
||||
)
|
||||
override val sessionId: String,
|
||||
val event: Event,
|
||||
override val lastFailureMessage: String? = null
|
||||
) : SessionWorkerParams
|
||||
|
||||
@Inject
|
||||
lateinit var sendVerificationMessageTask: SendVerificationMessageTask
|
||||
|
@ -49,10 +51,10 @@ internal class SendVerificationMessageWorker constructor(context: Context, param
|
|||
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||
?: return Result.success(errorOutputData)
|
||||
|
||||
val sessionComponent = getSessionComponent(params.userId)
|
||||
val sessionComponent = getSessionComponent(params.sessionId)
|
||||
?: return Result.success(errorOutputData).also {
|
||||
// TODO, can this happen? should I update local echo?
|
||||
Timber.e("Unknown Session, cannot send message, userId:${params.userId}")
|
||||
Timber.e("Unknown Session, cannot send message, sessionId: ${params.sessionId}")
|
||||
}
|
||||
sessionComponent.inject(this)
|
||||
val localId = params.event.eventId ?: ""
|
||||
|
|
|
@ -26,7 +26,7 @@ import javax.inject.Qualifier
|
|||
internal annotation class UserId
|
||||
|
||||
/**
|
||||
* Used to inject the userId
|
||||
* Used to inject the deviceId
|
||||
*/
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class WorkManagerProvider @Inject constructor(
|
||||
context: Context,
|
||||
@SessionId private val sessionId: String
|
||||
) {
|
||||
private val tag = MATRIX_SDK_TAG_PREFIX + sessionId
|
||||
|
||||
val workManager = WorkManager.getInstance(context)
|
||||
|
||||
/**
|
||||
* Create a OneTimeWorkRequestBuilder, with the Matrix SDK tag
|
||||
*/
|
||||
inline fun <reified W : ListenableWorker> matrixOneTimeWorkRequestBuilder() =
|
||||
OneTimeWorkRequestBuilder<W>()
|
||||
.addTag(tag)
|
||||
|
||||
/**
|
||||
* Cancel all works instantiated by the Matrix SDK for the current session, and not those from the SDK client, or for other sessions
|
||||
*/
|
||||
fun cancelAllWorks() {
|
||||
workManager.let {
|
||||
it.cancelAllWorkByTag(tag)
|
||||
it.pruneWork()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MATRIX_SDK_TAG_PREFIX = "MatrixSDK-"
|
||||
|
||||
/**
|
||||
* Default constraints: connected network
|
||||
*/
|
||||
val workConstraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.lifecycle.LiveData
|
||||
import dagger.Lazy
|
||||
|
@ -46,6 +45,7 @@ import im.vector.matrix.android.internal.auth.SessionParamsStore
|
|||
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||
import im.vector.matrix.android.internal.di.SessionId
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.session.sync.SyncTaskSequencer
|
||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||
import im.vector.matrix.android.internal.session.sync.job.SyncThread
|
||||
|
@ -63,7 +63,7 @@ import javax.inject.Provider
|
|||
@SessionScope
|
||||
internal class DefaultSession @Inject constructor(
|
||||
override val sessionParams: SessionParams,
|
||||
private val context: Context,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
private val eventBus: EventBus,
|
||||
@SessionId
|
||||
override val sessionId: String,
|
||||
|
@ -122,15 +122,15 @@ internal class DefaultSession @Inject constructor(
|
|||
}
|
||||
|
||||
override fun requireBackgroundSync() {
|
||||
SyncWorker.requireBackgroundSync(context, sessionId)
|
||||
SyncWorker.requireBackgroundSync(workManagerProvider, sessionId)
|
||||
}
|
||||
|
||||
override fun startAutomaticBackgroundSync(repeatDelay: Long) {
|
||||
SyncWorker.automaticallyBackgroundSync(context, sessionId, 0, repeatDelay)
|
||||
SyncWorker.automaticallyBackgroundSync(workManagerProvider, sessionId, 0, repeatDelay)
|
||||
}
|
||||
|
||||
override fun stopAnyBackgroundSync() {
|
||||
SyncWorker.stopAnyBackgroundSync(context)
|
||||
SyncWorker.stopAnyBackgroundSync(workManagerProvider)
|
||||
}
|
||||
|
||||
override fun startSync(fromForeground: Boolean) {
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session.group
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.WorkManager
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
||||
|
@ -27,8 +25,7 @@ import im.vector.matrix.android.internal.database.model.GroupEntity
|
|||
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.SessionId
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import io.realm.OrderedCollectionChangeSet
|
||||
import io.realm.RealmResults
|
||||
|
@ -38,7 +35,7 @@ import javax.inject.Inject
|
|||
private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
|
||||
|
||||
internal class GroupSummaryUpdater @Inject constructor(
|
||||
private val context: Context,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
@SessionId private val sessionId: String,
|
||||
private val monarchy: Monarchy)
|
||||
: RealmLiveEntityObserver<GroupEntity>(monarchy.realmConfiguration) {
|
||||
|
@ -72,12 +69,12 @@ internal class GroupSummaryUpdater @Inject constructor(
|
|||
|
||||
val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams)
|
||||
|
||||
val sendWork = matrixOneTimeWorkRequestBuilder<GetGroupDataWorker>()
|
||||
val sendWork = workManagerProvider.matrixOneTimeWorkRequestBuilder<GetGroupDataWorker>()
|
||||
.setInputData(workData)
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context)
|
||||
workManagerProvider.workManager
|
||||
.beginUniqueWork(GET_GROUP_DATA_WORKER, ExistingWorkPolicy.APPEND, sendWork)
|
||||
.enqueue()
|
||||
}
|
||||
|
|
|
@ -15,10 +15,8 @@
|
|||
*/
|
||||
package im.vector.matrix.android.internal.session.pushers
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.work.BackoffPolicy
|
||||
import androidx.work.WorkManager
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.pushers.Pusher
|
||||
|
@ -27,17 +25,16 @@ import im.vector.matrix.android.internal.database.mapper.asDomain
|
|||
import im.vector.matrix.android.internal.database.model.PusherEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.SessionId
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultPusherService @Inject constructor(
|
||||
private val context: Context,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
private val monarchy: Monarchy,
|
||||
@SessionId private val sessionId: String,
|
||||
private val getPusherTask: GetPushersTask,
|
||||
|
@ -68,12 +65,12 @@ internal class DefaultPusherService @Inject constructor(
|
|||
|
||||
val params = AddHttpPusherWorker.Params(sessionId, pusher)
|
||||
|
||||
val request = matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.setInputData(WorkerParamsFactory.toData(params))
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
workManagerProvider.workManager.enqueue(request)
|
||||
return request.id
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package im.vector.matrix.android.internal.session.room.relation
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
|
@ -46,15 +45,14 @@ import im.vector.matrix.android.internal.session.room.send.SendEventWorker
|
|||
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.CancelableWork
|
||||
import im.vector.matrix.android.internal.util.fetchCopyMap
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultRelationService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
private val context: Context,
|
||||
@SessionId private val sessionId: String,
|
||||
private val timeLineSendEventWorkCommon: TimelineSendEventWorkCommon,
|
||||
private val eventFactory: LocalEchoEventFactory,
|
||||
private val cryptoService: CryptoService,
|
||||
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
||||
|
@ -85,8 +83,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
|
||||
.also { saveLocalEcho(it) }
|
||||
val sendRelationWork = createSendEventWork(event, true)
|
||||
TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork)
|
||||
CancelableWork(context, sendRelationWork.id)
|
||||
timeLineSendEventWorkCommon.postWork(roomId, sendRelationWork)
|
||||
} else {
|
||||
Timber.w("Reaction already added")
|
||||
NoOpCancellable
|
||||
|
@ -111,7 +108,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
.also { saveLocalEcho(it) }
|
||||
val redactWork = createRedactEventWork(redactEvent, toRedact, null)
|
||||
|
||||
TimelineSendEventWorkCommon.postWork(context, roomId, redactWork)
|
||||
timeLineSendEventWorkCommon.postWork(roomId, redactWork)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +129,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
eventId,
|
||||
reason)
|
||||
val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
||||
return timeLineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
||||
}
|
||||
|
||||
override fun editTextMessage(targetEventId: String,
|
||||
|
@ -146,12 +143,10 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||
val workRequest = createSendEventWork(event, false)
|
||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
||||
CancelableWork(context, encryptWork.id)
|
||||
timeLineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, workRequest)
|
||||
} else {
|
||||
val workRequest = createSendEventWork(event, true)
|
||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||
CancelableWork(context, workRequest.id)
|
||||
timeLineSendEventWorkCommon.postWork(roomId, workRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,12 +163,10 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||
val workRequest = createSendEventWork(event, false)
|
||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
||||
CancelableWork(context, encryptWork.id)
|
||||
timeLineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, workRequest)
|
||||
} else {
|
||||
val workRequest = createSendEventWork(event, true)
|
||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||
CancelableWork(context, workRequest.id)
|
||||
timeLineSendEventWorkCommon.postWork(roomId, workRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,12 +187,10 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||
val workRequest = createSendEventWork(event, false)
|
||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
||||
CancelableWork(context, encryptWork.id)
|
||||
timeLineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, workRequest)
|
||||
} else {
|
||||
val workRequest = createSendEventWork(event, true)
|
||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||
CancelableWork(context, workRequest.id)
|
||||
timeLineSendEventWorkCommon.postWork(roomId, workRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,13 +198,13 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
// Same parameter
|
||||
val params = EncryptEventWorker.Params(sessionId, roomId, event, keepKeys)
|
||||
val sendWorkData = WorkerParamsFactory.toData(params)
|
||||
return TimelineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
|
||||
return timeLineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
|
||||
}
|
||||
|
||||
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event)
|
||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||
return timeLineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||
}
|
||||
|
||||
override fun getEventAnnotationsSummary(eventId: String): EventAnnotationsSummary? {
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session.room.send
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
import androidx.work.BackoffPolicy
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.Operation
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
|
@ -38,12 +40,11 @@ import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
|||
import im.vector.matrix.android.internal.database.query.findAllInRoomWithSendStates
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.SessionId
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.session.content.UploadContentWorker
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
|
||||
import im.vector.matrix.android.internal.util.CancelableWork
|
||||
import im.vector.matrix.android.internal.worker.AlwaysSuccessfulWorker
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import im.vector.matrix.android.internal.worker.startChain
|
||||
import timber.log.Timber
|
||||
|
@ -55,7 +56,8 @@ private const val BACKOFF_DELAY = 10_000L
|
|||
|
||||
internal class DefaultSendService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
private val context: Context,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
private val timelineSendEventWorkCommon: TimelineSendEventWorkCommon,
|
||||
@SessionId private val sessionId: String,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||
private val cryptoService: CryptoService,
|
||||
|
@ -91,12 +93,10 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
Timber.v("Send event in encrypted room")
|
||||
val encryptWork = createEncryptEventWork(event, true)
|
||||
val sendWork = createSendEventWork(event, false)
|
||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, sendWork)
|
||||
CancelableWork(context, encryptWork.id)
|
||||
timelineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, sendWork)
|
||||
} else {
|
||||
val sendWork = createSendEventWork(event, true)
|
||||
TimelineSendEventWorkCommon.postWork(context, roomId, sendWork)
|
||||
CancelableWork(context, sendWork.id)
|
||||
timelineSendEventWorkCommon.postWork(roomId, sendWork)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,8 +109,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
override fun redactEvent(event: Event, reason: String?): Cancelable {
|
||||
// TODO manage media/attachements?
|
||||
val redactWork = createRedactEventWork(event, reason)
|
||||
TimelineSendEventWorkCommon.postWork(context, roomId, redactWork)
|
||||
return CancelableWork(context, redactWork.id)
|
||||
return timelineSendEventWorkCommon.postWork(roomId, redactWork)
|
||||
}
|
||||
|
||||
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? {
|
||||
|
@ -168,16 +167,16 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun clearSendingQueue() {
|
||||
TimelineSendEventWorkCommon.cancelAllWorks(context, roomId)
|
||||
WorkManager.getInstance(context).cancelUniqueWork(buildWorkName(UPLOAD_WORK))
|
||||
timelineSendEventWorkCommon.cancelAllWorks(roomId)
|
||||
workManagerProvider.workManager.cancelUniqueWork(buildWorkName(UPLOAD_WORK))
|
||||
|
||||
// Replace the worker chains with a AlwaysSuccessfulWorker, to ensure the queues are well emptied
|
||||
matrixOneTimeWorkRequestBuilder<AlwaysSuccessfulWorker>()
|
||||
workManagerProvider.matrixOneTimeWorkRequestBuilder<AlwaysSuccessfulWorker>()
|
||||
.build().let {
|
||||
TimelineSendEventWorkCommon.postWork(context, roomId, it, ExistingWorkPolicy.REPLACE)
|
||||
timelineSendEventWorkCommon.postWork(roomId, it, ExistingWorkPolicy.REPLACE)
|
||||
|
||||
// need to clear also image sending queue
|
||||
WorkManager.getInstance(context)
|
||||
workManagerProvider.workManager
|
||||
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it)
|
||||
.enqueue()
|
||||
}
|
||||
|
@ -245,7 +244,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
return internalSendMedia(event, attachment)
|
||||
}
|
||||
|
||||
private fun internalSendMedia(localEcho: Event, attachment: ContentAttachmentData): CancelableWork {
|
||||
private fun internalSendMedia(localEcho: Event, attachment: ContentAttachmentData): Cancelable {
|
||||
val isRoomEncrypted = cryptoService.isRoomEncrypted(roomId)
|
||||
|
||||
val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true)
|
||||
|
@ -254,7 +253,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
if (isRoomEncrypted) {
|
||||
val encryptWork = createEncryptEventWork(localEcho, false /*not start of chain, take input error*/)
|
||||
|
||||
val op: Operation = WorkManager.getInstance(context)
|
||||
val op: Operation = workManagerProvider.workManager
|
||||
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
||||
.then(encryptWork)
|
||||
.then(sendWork)
|
||||
|
@ -267,13 +266,13 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
}
|
||||
}, workerFutureListenerExecutor)
|
||||
} else {
|
||||
WorkManager.getInstance(context)
|
||||
workManagerProvider.workManager
|
||||
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
||||
.then(sendWork)
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
return CancelableWork(context, sendWork.id)
|
||||
return CancelableWork(workManagerProvider.workManager, sendWork.id)
|
||||
}
|
||||
|
||||
private fun saveLocalEcho(event: Event) {
|
||||
|
@ -289,8 +288,8 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
val params = EncryptEventWorker.Params(sessionId, roomId, event)
|
||||
val sendWorkData = WorkerParamsFactory.toData(params)
|
||||
|
||||
return matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.setInputData(sendWorkData)
|
||||
.startChain(startChain)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||
|
@ -301,7 +300,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event)
|
||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||
|
||||
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||
}
|
||||
|
||||
private fun createRedactEventWork(event: Event, reason: String?): OneTimeWorkRequest {
|
||||
|
@ -310,7 +309,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
}
|
||||
val sendContentWorkerParams = RedactEventWorker.Params(sessionId, redactEvent.eventId!!, roomId, event.eventId, reason)
|
||||
val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
||||
return timelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
||||
}
|
||||
|
||||
private fun createUploadMediaWork(event: Event,
|
||||
|
@ -320,8 +319,8 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, roomId, event, attachment, isRoomEncrypted)
|
||||
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)
|
||||
|
||||
return matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.startChain(startChain)
|
||||
.setInputData(uploadWorkData)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||
|
|
|
@ -15,51 +15,54 @@
|
|||
*/
|
||||
package im.vector.matrix.android.internal.session.room.timeline
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.util.CancelableWork
|
||||
import im.vector.matrix.android.internal.worker.startChain
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val SEND_WORK = "SEND_WORK"
|
||||
private const val BACKOFF_DELAY = 10_000L
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Helper class for sending event related works.
|
||||
* All send event from a room are using the same workchain, in order to ensure order.
|
||||
* WorkRequest must always return success (even if server error, in this case marking the event as failed to send)
|
||||
* , if not the chain will be doomed in failed state.
|
||||
*
|
||||
* WorkRequest must always return success (even if server error, in this case marking the event as failed to send),
|
||||
* if not the chain will be doomed in failed state.
|
||||
*/
|
||||
internal object TimelineSendEventWorkCommon {
|
||||
internal class TimelineSendEventWorkCommon @Inject constructor(
|
||||
private val workManagerProvider: WorkManagerProvider
|
||||
) {
|
||||
|
||||
fun postSequentialWorks(context: Context, roomId: String, vararg workRequests: OneTimeWorkRequest) {
|
||||
when {
|
||||
workRequests.isEmpty() -> return
|
||||
workRequests.size == 1 -> postWork(context, roomId, workRequests.first())
|
||||
fun postSequentialWorks(roomId: String, vararg workRequests: OneTimeWorkRequest): Cancelable {
|
||||
return when {
|
||||
workRequests.isEmpty() -> NoOpCancellable
|
||||
workRequests.size == 1 -> postWork(roomId, workRequests.first())
|
||||
else -> {
|
||||
val firstWork = workRequests.first()
|
||||
var continuation = WorkManager.getInstance(context)
|
||||
var continuation = workManagerProvider.workManager
|
||||
.beginUniqueWork(buildWorkName(roomId), ExistingWorkPolicy.APPEND, firstWork)
|
||||
for (i in 1 until workRequests.size) {
|
||||
val workRequest = workRequests[i]
|
||||
continuation = continuation.then(workRequest)
|
||||
}
|
||||
continuation.enqueue()
|
||||
CancelableWork(workManagerProvider.workManager, firstWork.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun postWork(context: Context, roomId: String, workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND) {
|
||||
WorkManager.getInstance(context)
|
||||
fun postWork(roomId: String, workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND): Cancelable {
|
||||
workManagerProvider.workManager
|
||||
.beginUniqueWork(buildWorkName(roomId), policy, workRequest)
|
||||
.enqueue()
|
||||
|
||||
return CancelableWork(workManagerProvider.workManager, workRequest.id)
|
||||
}
|
||||
|
||||
inline fun <reified W : ListenableWorker> createWork(data: Data, startChain: Boolean): OneTimeWorkRequest {
|
||||
return matrixOneTimeWorkRequestBuilder<W>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<W>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.startChain(startChain)
|
||||
.setInputData(data)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||
|
@ -70,7 +73,13 @@ internal object TimelineSendEventWorkCommon {
|
|||
return "${roomId}_$SEND_WORK"
|
||||
}
|
||||
|
||||
fun cancelAllWorks(context: Context, roomId: String) {
|
||||
WorkManager.getInstance(context).cancelUniqueWork(buildWorkName(roomId))
|
||||
fun cancelAllWorks(roomId: String) {
|
||||
workManagerProvider.workManager
|
||||
.cancelUniqueWork(buildWorkName(roomId))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SEND_WORK = "SEND_WORK"
|
||||
private const val BACKOFF_DELAY = 10_000L
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session.signout
|
||||
|
||||
import android.content.Context
|
||||
import im.vector.matrix.android.BuildConfig
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.failure.MatrixError
|
||||
|
@ -29,7 +28,6 @@ import im.vector.matrix.android.internal.network.executeRequest
|
|||
import im.vector.matrix.android.internal.session.SessionModule
|
||||
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
@ -45,7 +43,7 @@ internal interface SignOutTask : Task<SignOutTask.Params, Unit> {
|
|||
}
|
||||
|
||||
internal class DefaultSignOutTask @Inject constructor(
|
||||
private val context: Context,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
@SessionId private val sessionId: String,
|
||||
private val signOutAPI: SignOutAPI,
|
||||
private val sessionManager: SessionManager,
|
||||
|
@ -87,7 +85,7 @@ internal class DefaultSignOutTask @Inject constructor(
|
|||
sessionManager.releaseSession(sessionId)
|
||||
|
||||
Timber.d("SignOut: cancel pending works...")
|
||||
WorkManagerUtil.cancelAllWorks(context)
|
||||
workManagerProvider.cancelAllWorks()
|
||||
|
||||
Timber.d("SignOut: delete session params...")
|
||||
sessionParamsStore.delete(sessionId)
|
||||
|
|
|
@ -16,15 +16,17 @@
|
|||
package im.vector.matrix.android.internal.session.sync.job
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
import androidx.work.BackoffPolicy
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.WorkerParameters
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.failure.isTokenError
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||
import im.vector.matrix.android.internal.session.sync.SyncTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||
import timber.log.Timber
|
||||
|
@ -75,30 +77,33 @@ internal class SyncWorker(context: Context,
|
|||
|
||||
companion object {
|
||||
|
||||
const val BG_SYNC_WORK_NAME = "BG_SYNCP"
|
||||
private const val BG_SYNC_WORK_NAME = "BG_SYNCP"
|
||||
|
||||
fun requireBackgroundSync(context: Context, sessionId: String, serverTimeout: Long = 0) {
|
||||
fun requireBackgroundSync(workManagerProvider: WorkManagerProvider, sessionId: String, serverTimeout: Long = 0) {
|
||||
val data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, false))
|
||||
val workRequest = matrixOneTimeWorkRequestBuilder<SyncWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 1_000, TimeUnit.MILLISECONDS)
|
||||
.setInputData(data)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest)
|
||||
workManagerProvider.workManager
|
||||
.enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest)
|
||||
}
|
||||
|
||||
fun automaticallyBackgroundSync(context: Context, sessionId: String, serverTimeout: Long = 0, delay: Long = 30_000) {
|
||||
fun automaticallyBackgroundSync(workManagerProvider: WorkManagerProvider, sessionId: String, serverTimeout: Long = 0, delay: Long = 30_000) {
|
||||
val data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, true))
|
||||
val workRequest = matrixOneTimeWorkRequestBuilder<SyncWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.setInputData(data)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, delay, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest)
|
||||
workManagerProvider.workManager
|
||||
.enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest)
|
||||
}
|
||||
|
||||
fun stopAnyBackgroundSync(context: Context) {
|
||||
WorkManager.getInstance(context).cancelUniqueWork(BG_SYNC_WORK_NAME)
|
||||
fun stopAnyBackgroundSync(workManagerProvider: WorkManagerProvider) {
|
||||
workManagerProvider.workManager
|
||||
.cancelUniqueWork(BG_SYNC_WORK_NAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,15 +16,14 @@
|
|||
|
||||
package im.vector.matrix.android.internal.util
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.WorkManager
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
|
||||
internal class CancelableWork(private val context: Context,
|
||||
internal class CancelableWork(private val workManager: WorkManager,
|
||||
private val workId: UUID) : Cancelable {
|
||||
|
||||
override fun cancel() {
|
||||
WorkManager.getInstance(context).cancelWorkById(workId)
|
||||
workManager.cancelWorkById(workId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* 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.worker
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
|
||||
// TODO Multiaccount
|
||||
internal object WorkManagerUtil {
|
||||
private const val MATRIX_SDK_TAG = "MatrixSDK"
|
||||
|
||||
/**
|
||||
* Default constraints: connected network
|
||||
*/
|
||||
val workConstraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
|
||||
/**
|
||||
* Create a OneTimeWorkRequestBuilder, with the Matrix SDK tag
|
||||
*/
|
||||
inline fun <reified W : ListenableWorker> matrixOneTimeWorkRequestBuilder() =
|
||||
OneTimeWorkRequestBuilder<W>()
|
||||
.addTag(MATRIX_SDK_TAG)
|
||||
|
||||
/**
|
||||
* Cancel all works instantiated by the Matrix SDK and not those from the SDK client
|
||||
*/
|
||||
fun cancelAllWorks(context: Context) {
|
||||
WorkManager.getInstance(context).also {
|
||||
it.cancelAllWorkByTag(MATRIX_SDK_TAG)
|
||||
it.pruneWork()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,9 +47,6 @@
|
|||
android:label="@string/title_activity_settings"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity android:name=".features.media.VideoMediaViewerActivity" />
|
||||
<activity
|
||||
android:name=".features.crypto.verification.SASVerificationActivity"
|
||||
android:label="@string/title_activity_verify_device" />
|
||||
<activity
|
||||
android:name=".features.crypto.keysbackup.restore.KeysBackupRestoreActivity"
|
||||
android:label="@string/title_activity_keys_backup_setup" />
|
||||
|
|
|
@ -23,7 +23,10 @@ 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.*
|
||||
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||
import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
|
||||
import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment
|
||||
import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
|
||||
import im.vector.riotx.features.home.HomeDetailFragment
|
||||
import im.vector.riotx.features.home.HomeDrawerFragment
|
||||
import im.vector.riotx.features.home.LoadingFragment
|
||||
|
@ -239,26 +242,6 @@ interface FragmentModule {
|
|||
@FragmentKey(VectorSettingsDevicesFragment::class)
|
||||
fun bindVectorSettingsDevicesFragment(fragment: VectorSettingsDevicesFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(SASVerificationIncomingFragment::class)
|
||||
fun bindSASVerificationIncomingFragment(fragment: SASVerificationIncomingFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(SASVerificationShortCodeFragment::class)
|
||||
fun bindSASVerificationShortCodeFragment(fragment: SASVerificationShortCodeFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(SASVerificationVerifiedFragment::class)
|
||||
fun bindSASVerificationVerifiedFragment(fragment: SASVerificationVerifiedFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(SASVerificationStartFragment::class)
|
||||
fun bindSASVerificationStartFragment(fragment: SASVerificationStartFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(PublicRoomsFragment::class)
|
||||
|
@ -302,15 +285,15 @@ interface FragmentModule {
|
|||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(VerificationChooseMethodFragment::class)
|
||||
fun bindVerificationMethodChooserFragment(fragment: VerificationChooseMethodFragment): Fragment
|
||||
fun bindVerificationChooseMethodFragment(fragment: VerificationChooseMethodFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(SASVerificationCodeFragment::class)
|
||||
fun bindVerificationSasCodeFragment(fragment: SASVerificationCodeFragment): Fragment
|
||||
@FragmentKey(VerificationEmojiCodeFragment::class)
|
||||
fun bindVerificationEmojiCodeFragment(fragment: VerificationEmojiCodeFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(VerificationConclusionFragment::class)
|
||||
fun bindVerificationSasConclusionFragment(fragment: VerificationConclusionFragment): Fragment
|
||||
fun bindVerificationConclusionFragment(fragment: VerificationConclusionFragment): Fragment
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromK
|
|||
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
|
||||
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
|
||||
import im.vector.riotx.features.crypto.verification.SasVerificationViewModel
|
||||
import im.vector.riotx.features.home.HomeSharedActionViewModel
|
||||
import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel
|
||||
import im.vector.riotx.features.home.room.detail.timeline.action.MessageSharedActionViewModel
|
||||
|
@ -61,11 +60,6 @@ interface ViewModelModule {
|
|||
@ViewModelKey(EmojiChooserViewModel::class)
|
||||
fun bindEmojiChooserViewModel(viewModel: EmojiChooserViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(SasVerificationViewModel::class)
|
||||
fun bindSasVerificationViewModel(viewModel: SasVerificationViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(KeysBackupRestoreFromKeyViewModel::class)
|
||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.riotx.core.epoxy
|
|||
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
|
@ -33,6 +34,7 @@ abstract class ErrorWithRetryItem : VectorEpoxyModel<ErrorWithRetryItem.Holder>(
|
|||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.textView.text = text
|
||||
holder.buttonView.isVisible = listener != null
|
||||
holder.buttonView.setOnClickListener { listener?.invoke() }
|
||||
}
|
||||
|
||||
|
|
|
@ -28,12 +28,17 @@ fun RecyclerView.configureWith(epoxyController: EpoxyController,
|
|||
itemAnimator: RecyclerView.ItemAnimator? = null,
|
||||
viewPool: RecyclerView.RecycledViewPool? = null,
|
||||
showDivider: Boolean = false,
|
||||
hasFixedSize: Boolean = true) {
|
||||
hasFixedSize: Boolean = true,
|
||||
disableItemAnimation: Boolean = false) {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false).apply {
|
||||
recycleChildrenOnDetach = viewPool != null
|
||||
}
|
||||
setRecycledViewPool(viewPool)
|
||||
itemAnimator?.let { this.itemAnimator = it }
|
||||
if (disableItemAnimation) {
|
||||
this.itemAnimator = null
|
||||
} else {
|
||||
itemAnimator?.let { this.itemAnimator = it }
|
||||
}
|
||||
if (showDivider) {
|
||||
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
|
||||
}
|
||||
|
|
|
@ -102,9 +102,11 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
|
|||
private lateinit var sessionListener: SessionListener
|
||||
protected lateinit var bugReporter: BugReporter
|
||||
lateinit var rageShake: RageShake
|
||||
|
||||
lateinit var navigator: Navigator
|
||||
private set
|
||||
protected lateinit var navigator: Navigator
|
||||
private lateinit var fragmentFactory: FragmentFactory
|
||||
|
||||
private lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
private lateinit var vectorPreferences: VectorPreferences
|
||||
|
||||
|
@ -210,8 +212,8 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
|
|||
handleInvalidToken(globalError)
|
||||
is GlobalError.ConsentNotGivenError ->
|
||||
consentNotGivenHelper.displayDialog(globalError.consentUri,
|
||||
activeSessionHolder.getActiveSession().sessionParams.homeServerConnectionConfig.homeServerUri.host
|
||||
?: "")
|
||||
activeSessionHolder.getActiveSession().sessionParams.homeServerConnectionConfig.homeServerUri.host
|
||||
?: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,16 @@ import android.app.Dialog
|
|||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.Unbinder
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.MvRxView
|
||||
import com.airbnb.mvrx.MvRxViewId
|
||||
|
@ -43,6 +49,15 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
|
|||
final override val mvrxViewId: String by mvrxViewIdProperty
|
||||
private lateinit var screenComponent: ScreenComponent
|
||||
|
||||
/* ==========================================================================================
|
||||
* View
|
||||
* ========================================================================================== */
|
||||
|
||||
@LayoutRes
|
||||
abstract fun getLayoutResId(): Int
|
||||
|
||||
private var unBinder: Unbinder? = null
|
||||
|
||||
/* ==========================================================================================
|
||||
* View model
|
||||
* ========================================================================================== */
|
||||
|
@ -67,6 +82,18 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
|
|||
|
||||
open val showExpanded = false
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(getLayoutResId(), container, false)
|
||||
unBinder = ButterKnife.bind(this, view)
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
unBinder?.unbind()
|
||||
unBinder = null
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
|
||||
viewModelFactory = screenComponent.viewModelFactory()
|
||||
|
|
|
@ -98,10 +98,9 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
// Don't activate animation as we might have way to much item animation when filtering
|
||||
recyclerView.itemAnimator = null
|
||||
knownUsersController.callback = this
|
||||
recyclerView.configureWith(knownUsersController)
|
||||
// Don't activate animation as we might have way to much item animation when filtering
|
||||
recyclerView.configureWith(knownUsersController, disableItemAnimation = true)
|
||||
}
|
||||
|
||||
private fun setupFilterView() {
|
||||
|
|
|
@ -33,13 +33,11 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
|||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.features.crypto.verification.SASVerificationActivity
|
||||
import im.vector.riotx.features.popup.PopupAlertManager
|
||||
import timber.log.Timber
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.Date
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.collections.ArrayList
|
||||
|
@ -195,18 +193,19 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
|
|||
denyAllRequests(mappingKey)
|
||||
}
|
||||
|
||||
alert.addButton(
|
||||
context.getString(R.string.start_verification_short_label),
|
||||
Runnable {
|
||||
alert.weakCurrentActivity?.get()?.let {
|
||||
val intent = SASVerificationActivity.outgoingIntent(it,
|
||||
session?.myUserId ?: "",
|
||||
userId, deviceId)
|
||||
it.startActivity(intent)
|
||||
}
|
||||
},
|
||||
false
|
||||
)
|
||||
// TODO send to the new profile page
|
||||
// alert.addButton(
|
||||
// context.getString(R.string.start_verification_short_label),
|
||||
// Runnable {
|
||||
// alert.weakCurrentActivity?.get()?.let {
|
||||
// val intent = SASVerificationActivity.outgoingIntent(it,
|
||||
// session?.myUserId ?: "",
|
||||
// userId, deviceId)
|
||||
// it.startActivity(intent)
|
||||
// }
|
||||
// },
|
||||
// false
|
||||
// )
|
||||
|
||||
alert.addButton(context.getString(R.string.share_without_verifying_short_label), Runnable {
|
||||
shareAllSessions(mappingKey)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.crypto.verification
|
||||
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
||||
|
||||
// TODO Add support for SCAN (QR code)
|
||||
val supportedVerificationMethods = listOf(VerificationMethod.SAS)
|
|
@ -20,8 +20,13 @@ import im.vector.matrix.android.api.session.Session
|
|||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
|
||||
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
||||
import im.vector.riotx.features.popup.PopupAlertManager
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -48,46 +53,46 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
|
|||
override fun transactionUpdated(tx: SasVerificationTransaction) {
|
||||
when (tx.state) {
|
||||
SasVerificationTxState.OnStarted -> {
|
||||
// Add a notification for every incoming request
|
||||
val name = session?.getUser(tx.otherUserId)?.displayName
|
||||
?: tx.otherUserId
|
||||
|
||||
val alert = PopupAlertManager.VectorAlert(
|
||||
"kvr_${tx.transactionId}",
|
||||
context.getString(R.string.sas_incoming_request_notif_title),
|
||||
context.getString(R.string.sas_incoming_request_notif_content, name),
|
||||
R.drawable.shield)
|
||||
.apply {
|
||||
contentAction = Runnable {
|
||||
val intent = SASVerificationActivity.incomingIntent(context,
|
||||
session?.myUserId ?: "",
|
||||
tx.otherUserId,
|
||||
tx.transactionId)
|
||||
weakCurrentActivity?.get()?.startActivity(intent)
|
||||
}
|
||||
dismissedAction = Runnable {
|
||||
tx.cancel()
|
||||
}
|
||||
addButton(
|
||||
context.getString(R.string.ignore),
|
||||
Runnable {
|
||||
tx.cancel()
|
||||
}
|
||||
)
|
||||
addButton(
|
||||
context.getString(R.string.action_open),
|
||||
Runnable {
|
||||
val intent = SASVerificationActivity.incomingIntent(context,
|
||||
session?.myUserId ?: "",
|
||||
tx.otherUserId,
|
||||
tx.transactionId)
|
||||
weakCurrentActivity?.get()?.startActivity(intent)
|
||||
}
|
||||
)
|
||||
// 10mn expiration
|
||||
expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L)
|
||||
}
|
||||
PopupAlertManager.postVectorAlert(alert)
|
||||
// // Add a notification for every incoming request
|
||||
// val name = session?.getUser(tx.otherUserId)?.displayName
|
||||
// ?: tx.otherUserId
|
||||
//
|
||||
// val alert = PopupAlertManager.VectorAlert(
|
||||
// "kvr_${tx.transactionId}",
|
||||
// context.getString(R.string.sas_incoming_request_notif_title),
|
||||
// context.getString(R.string.sas_incoming_request_notif_content, name),
|
||||
// R.drawable.shield)
|
||||
// .apply {
|
||||
// contentAction = Runnable {
|
||||
// val intent = SASVerificationActivity.incomingIntent(context,
|
||||
// session?.myUserId ?: "",
|
||||
// tx.otherUserId,
|
||||
// tx.transactionId)
|
||||
// weakCurrentActivity?.get()?.startActivity(intent)
|
||||
// }
|
||||
// dismissedAction = Runnable {
|
||||
// tx.cancel()
|
||||
// }
|
||||
// addButton(
|
||||
// context.getString(R.string.ignore),
|
||||
// Runnable {
|
||||
// tx.cancel()
|
||||
// }
|
||||
// )
|
||||
// addButton(
|
||||
// context.getString(R.string.action_open),
|
||||
// Runnable {
|
||||
// val intent = SASVerificationActivity.incomingIntent(context,
|
||||
// session?.myUserId ?: "",
|
||||
// tx.otherUserId,
|
||||
// tx.transactionId)
|
||||
// weakCurrentActivity?.get()?.startActivity(intent)
|
||||
// }
|
||||
// )
|
||||
// // 10mn expiration
|
||||
// expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L)
|
||||
// }
|
||||
// PopupAlertManager.postVectorAlert(alert)
|
||||
}
|
||||
SasVerificationTxState.Cancelled,
|
||||
SasVerificationTxState.OnCancelled,
|
||||
|
@ -101,4 +106,54 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
|
|||
|
||||
override fun markedAsManuallyVerified(userId: String, deviceId: String) {
|
||||
}
|
||||
|
||||
override fun verificationRequestCreated(pr: PendingVerificationRequest) {
|
||||
// For incoming request we should prompt (if not in activity where this request apply)
|
||||
if (pr.isIncoming) {
|
||||
val name = session?.getUser(pr.otherUserId)?.displayName
|
||||
?: pr.otherUserId
|
||||
|
||||
val alert = PopupAlertManager.VectorAlert(
|
||||
uniqueIdForVerificationRequest(pr),
|
||||
context.getString(R.string.sas_incoming_request_notif_title),
|
||||
"$name(${pr.otherUserId})",
|
||||
R.drawable.ic_shield_black,
|
||||
shouldBeDisplayedIn = { activity ->
|
||||
if (activity is RoomDetailActivity) {
|
||||
activity.intent?.extras?.getParcelable<RoomDetailArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
|
||||
it.roomId != pr.roomId
|
||||
} ?: true
|
||||
} else true
|
||||
})
|
||||
.apply {
|
||||
contentAction = Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||
it.navigator.openRoom(it, pr.roomId ?: "", pr.transactionId)
|
||||
}
|
||||
}
|
||||
dismissedAction = Runnable {
|
||||
session?.getSasVerificationService()?.declineVerificationRequestInDMs(pr.otherUserId,
|
||||
pr.requestInfo?.fromDevice ?: "",
|
||||
pr.transactionId ?: "",
|
||||
pr.roomId ?: ""
|
||||
)
|
||||
}
|
||||
colorInt = ThemeUtils.getColor(context, R.attr.vctr_notice_secondary)
|
||||
// 5mn expiration
|
||||
expirationTimestamp = System.currentTimeMillis() + (5 * 60 * 1000L)
|
||||
}
|
||||
PopupAlertManager.postVectorAlert(alert)
|
||||
}
|
||||
}
|
||||
|
||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
||||
// If an incoming request is readied (by another device?) we should discard the alert
|
||||
if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession)) {
|
||||
PopupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr))
|
||||
}
|
||||
super.verificationRequestUpdated(pr)
|
||||
}
|
||||
|
||||
private fun uniqueIdForVerificationRequest(pr: PendingVerificationRequest) =
|
||||
"verificationRequest_${pr.transactionId}"
|
||||
}
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.lifecycle.Observer
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.commitTransaction
|
||||
import im.vector.riotx.core.extensions.observeEvent
|
||||
import im.vector.riotx.core.platform.SimpleFragmentActivity
|
||||
import im.vector.riotx.core.platform.WaitingViewData
|
||||
|
||||
// TODO Deprecated("replaced by bottomsheet UX")
|
||||
class SASVerificationActivity : SimpleFragmentActivity() {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_MATRIX_ID = "EXTRA_MATRIX_ID"
|
||||
private const val EXTRA_TRANSACTION_ID = "EXTRA_TRANSACTION_ID"
|
||||
private const val EXTRA_OTHER_USER_ID = "EXTRA_OTHER_USER_ID"
|
||||
private const val EXTRA_OTHER_DEVICE_ID = "EXTRA_OTHER_DEVICE_ID"
|
||||
private const val EXTRA_IS_INCOMING = "EXTRA_IS_INCOMING"
|
||||
|
||||
/* ==========================================================================================
|
||||
* INPUT
|
||||
* ========================================================================================== */
|
||||
|
||||
fun incomingIntent(context: Context, matrixID: String, otherUserId: String, transactionID: String): Intent {
|
||||
val intent = Intent(context, SASVerificationActivity::class.java)
|
||||
intent.putExtra(EXTRA_MATRIX_ID, matrixID)
|
||||
intent.putExtra(EXTRA_TRANSACTION_ID, transactionID)
|
||||
intent.putExtra(EXTRA_OTHER_USER_ID, otherUserId)
|
||||
intent.putExtra(EXTRA_IS_INCOMING, true)
|
||||
return intent
|
||||
}
|
||||
|
||||
fun outgoingIntent(context: Context, matrixID: String, otherUserId: String, otherDeviceId: String): Intent {
|
||||
val intent = Intent(context, SASVerificationActivity::class.java)
|
||||
intent.putExtra(EXTRA_MATRIX_ID, matrixID)
|
||||
intent.putExtra(EXTRA_OTHER_DEVICE_ID, otherDeviceId)
|
||||
intent.putExtra(EXTRA_OTHER_USER_ID, otherUserId)
|
||||
intent.putExtra(EXTRA_IS_INCOMING, false)
|
||||
return intent
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* OUTPUT
|
||||
* ========================================================================================== */
|
||||
|
||||
fun getOtherUserId(intent: Intent?): String? {
|
||||
return intent?.getStringExtra(EXTRA_OTHER_USER_ID)
|
||||
}
|
||||
|
||||
fun getOtherDeviceId(intent: Intent?): String? {
|
||||
return intent?.getStringExtra(EXTRA_OTHER_DEVICE_ID)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTitleRes() = R.string.title_activity_verify_device
|
||||
|
||||
private lateinit var viewModel: SasVerificationViewModel
|
||||
|
||||
override fun initUiAndData() {
|
||||
super.initUiAndData()
|
||||
viewModel = viewModelProvider.get(SasVerificationViewModel::class.java)
|
||||
val transactionID: String? = intent.getStringExtra(EXTRA_TRANSACTION_ID)
|
||||
|
||||
if (isFirstCreation()) {
|
||||
val isIncoming = intent.getBooleanExtra(EXTRA_IS_INCOMING, false)
|
||||
if (isIncoming) {
|
||||
// incoming always have a transaction id
|
||||
viewModel.initIncoming(session, intent.getStringExtra(EXTRA_OTHER_USER_ID), transactionID)
|
||||
} else {
|
||||
viewModel.initOutgoing(session, intent.getStringExtra(EXTRA_OTHER_USER_ID), intent.getStringExtra(EXTRA_OTHER_DEVICE_ID))
|
||||
}
|
||||
|
||||
if (isIncoming) {
|
||||
val incoming = viewModel.transaction as? IncomingSasVerificationTransaction
|
||||
when (incoming?.uxState) {
|
||||
null,
|
||||
IncomingSasVerificationTransaction.UxState.UNKNOWN,
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT,
|
||||
IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT -> {
|
||||
supportActionBar?.setTitle(R.string.sas_incoming_request_title)
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
|
||||
replace(R.id.container, SASVerificationIncomingFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.WAIT_FOR_VERIFICATION,
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
|
||||
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
|
||||
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME,
|
||||
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> {
|
||||
viewModel.navigateCancel()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val outgoing = viewModel.transaction as? OutgoingSasVerificationRequest
|
||||
// transaction can be null, as not yet created
|
||||
when (outgoing?.uxState) {
|
||||
null,
|
||||
OutgoingSasVerificationRequest.UxState.UNKNOWN,
|
||||
OutgoingSasVerificationRequest.UxState.WAIT_FOR_START,
|
||||
OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> {
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
|
||||
replace(R.id.container, SASVerificationStartFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.SHOW_SAS,
|
||||
OutgoingSasVerificationRequest.UxState.WAIT_FOR_VERIFICATION -> {
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
|
||||
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.VERIFIED -> {
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
|
||||
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
|
||||
viewModel.navigateCancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.navigateEvent.observeEvent(this) { uxStateEvent ->
|
||||
when (uxStateEvent) {
|
||||
SasVerificationViewModel.NAVIGATE_FINISH -> {
|
||||
finish()
|
||||
}
|
||||
SasVerificationViewModel.NAVIGATE_FINISH_SUCCESS -> {
|
||||
val dataResult = Intent()
|
||||
dataResult.putExtra(EXTRA_OTHER_DEVICE_ID, viewModel.otherDeviceId)
|
||||
dataResult.putExtra(EXTRA_OTHER_USER_ID, viewModel.otherUserId)
|
||||
setResult(Activity.RESULT_OK, dataResult)
|
||||
finish()
|
||||
}
|
||||
SasVerificationViewModel.NAVIGATE_SAS_DISPLAY -> {
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out)
|
||||
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
SasVerificationViewModel.NAVIGATE_SUCCESS -> {
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out)
|
||||
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
|
||||
}
|
||||
}
|
||||
SasVerificationViewModel.NAVIGATE_CANCELLED -> {
|
||||
val isCancelledByMe = viewModel.transaction?.state == SasVerificationTxState.Cancelled
|
||||
val humanReadableReason = when (viewModel.transaction?.cancelledReason) {
|
||||
CancelCode.User -> getString(R.string.sas_error_m_user)
|
||||
CancelCode.Timeout -> getString(R.string.sas_error_m_timeout)
|
||||
CancelCode.UnknownTransaction -> getString(R.string.sas_error_m_unknown_transaction)
|
||||
CancelCode.UnknownMethod -> getString(R.string.sas_error_m_unknown_method)
|
||||
CancelCode.MismatchedCommitment -> getString(R.string.sas_error_m_mismatched_commitment)
|
||||
CancelCode.MismatchedSas -> getString(R.string.sas_error_m_mismatched_sas)
|
||||
CancelCode.UnexpectedMessage -> getString(R.string.sas_error_m_unexpected_message)
|
||||
CancelCode.InvalidMessage -> getString(R.string.sas_error_m_invalid_message)
|
||||
CancelCode.MismatchedKeys -> getString(R.string.sas_error_m_key_mismatch)
|
||||
// Use user error
|
||||
CancelCode.UserMismatchError -> getString(R.string.sas_error_m_user_error)
|
||||
null -> getString(R.string.sas_error_unknown)
|
||||
}
|
||||
val message =
|
||||
if (isCancelledByMe) getString(R.string.sas_cancelled_by_me, humanReadableReason)
|
||||
else getString(R.string.sas_cancelled_by_other, humanReadableReason)
|
||||
// Show a dialog
|
||||
if (!this.isFinishing) {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.sas_cancelled_dialog_title)
|
||||
.setMessage(message)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
// nop
|
||||
finish()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.loadingLiveEvent.observe(this, Observer {
|
||||
if (it == null) {
|
||||
hideWaitingView()
|
||||
} else {
|
||||
val status = if (it == -1) "" else getString(it)
|
||||
updateWaitingView(WaitingViewData(status, isIndeterminate = true))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
// we want to cancel the transaction
|
||||
viewModel.cancelTransaction()
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
// we want to cancel the transaction
|
||||
viewModel.cancelTransaction()
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import com.airbnb.mvrx.*
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import kotlinx.android.synthetic.main.fragment_bottom_sas_verification_code.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class SASVerificationCodeFragment @Inject constructor(
|
||||
val viewModelFactory: SASVerificationCodeViewModel.Factory
|
||||
) : VectorBaseFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_bottom_sas_verification_code
|
||||
|
||||
@BindView(R.id.sas_emoji_grid)
|
||||
lateinit var emojiGrid: ViewGroup
|
||||
|
||||
@BindView(R.id.sas_decimal_code)
|
||||
lateinit var decimalTextView: TextView
|
||||
|
||||
@BindView(R.id.emoji0)
|
||||
lateinit var emoji0View: ViewGroup
|
||||
@BindView(R.id.emoji1)
|
||||
lateinit var emoji1View: ViewGroup
|
||||
@BindView(R.id.emoji2)
|
||||
lateinit var emoji2View: ViewGroup
|
||||
@BindView(R.id.emoji3)
|
||||
lateinit var emoji3View: ViewGroup
|
||||
@BindView(R.id.emoji4)
|
||||
lateinit var emoji4View: ViewGroup
|
||||
@BindView(R.id.emoji5)
|
||||
lateinit var emoji5View: ViewGroup
|
||||
@BindView(R.id.emoji6)
|
||||
lateinit var emoji6View: ViewGroup
|
||||
|
||||
private val viewModel by fragmentViewModel(SASVerificationCodeViewModel::class)
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
|
||||
if (state.supportsEmoji) {
|
||||
decimalTextView.isVisible = false
|
||||
when (val emojiDescription = state.emojiDescription) {
|
||||
is Success -> {
|
||||
sasLoadingProgress.isVisible = false
|
||||
emojiGrid.isVisible = true
|
||||
ButtonsVisibilityGroup.isVisible = true
|
||||
emojiDescription.invoke().forEachIndexed { index, emojiRepresentation ->
|
||||
when (index) {
|
||||
0 -> {
|
||||
emoji0View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji0View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
1 -> {
|
||||
emoji1View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji1View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
2 -> {
|
||||
emoji2View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji2View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
3 -> {
|
||||
emoji3View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji3View.findViewById<TextView>(R.id.item_emoji_name_tv)?.setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
4 -> {
|
||||
emoji4View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji4View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
5 -> {
|
||||
emoji5View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji5View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
6 -> {
|
||||
emoji6View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji6View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.isWaitingFromOther) {
|
||||
// hide buttons
|
||||
ButtonsVisibilityGroup.isInvisible = true
|
||||
sasCodeWaitingPartnerText.isVisible = true
|
||||
} else {
|
||||
ButtonsVisibilityGroup.isVisible = true
|
||||
sasCodeWaitingPartnerText.isVisible = false
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
sasLoadingProgress.isVisible = false
|
||||
emojiGrid.isInvisible = true
|
||||
ButtonsVisibilityGroup.isInvisible = true
|
||||
// TODO?
|
||||
}
|
||||
else -> {
|
||||
sasLoadingProgress.isVisible = true
|
||||
emojiGrid.isInvisible = true
|
||||
ButtonsVisibilityGroup.isInvisible = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Decimal
|
||||
emojiGrid.isInvisible = true
|
||||
decimalTextView.isVisible = true
|
||||
val decimalCode = state.decimalDescription.invoke()
|
||||
decimalTextView.text = decimalCode
|
||||
|
||||
// TODO
|
||||
if (state.isWaitingFromOther) {
|
||||
// hide buttons
|
||||
ButtonsVisibilityGroup.isInvisible = true
|
||||
sasCodeWaitingPartnerText.isVisible = true
|
||||
} else {
|
||||
ButtonsVisibilityGroup.isVisible = decimalCode != null
|
||||
sasCodeWaitingPartnerText.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_request_continue_button)
|
||||
fun onMatchButtonTapped() = withState(viewModel) { state ->
|
||||
val otherUserId = state.otherUser?.id ?: return@withState
|
||||
val txId = state.transactionId ?: return@withState
|
||||
// UX echo
|
||||
ButtonsVisibilityGroup.isInvisible = true
|
||||
sasCodeWaitingPartnerText.isVisible = true
|
||||
sharedViewModel.handle(VerificationAction.SASMatchAction(otherUserId, txId))
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_request_cancel_button)
|
||||
fun onDoNotMatchButtonTapped() = withState(viewModel) { state ->
|
||||
val otherUserId = state.otherUser?.id ?: return@withState
|
||||
val txId = state.transactionId ?: return@withState
|
||||
// UX echo
|
||||
ButtonsVisibilityGroup.isInvisible = true
|
||||
sasCodeWaitingPartnerText.isVisible = true
|
||||
sharedViewModel.handle(VerificationAction.SASDoNotMatchAction(otherUserId, txId))
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.Observer
|
||||
import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
|
||||
import im.vector.matrix.android.api.util.MatrixItem
|
||||
import im.vector.matrix.android.api.util.toMatrixItem
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO Deprecated("replaced by bottomsheet UX")
|
||||
class SASVerificationIncomingFragment @Inject constructor(
|
||||
private var avatarRenderer: AvatarRenderer
|
||||
) : VectorBaseFragment() {
|
||||
|
||||
@BindView(R.id.sas_incoming_request_user_display_name)
|
||||
lateinit var otherUserDisplayNameTextView: TextView
|
||||
|
||||
@BindView(R.id.sas_incoming_request_user_id)
|
||||
lateinit var otherUserIdTextView: TextView
|
||||
|
||||
@BindView(R.id.sas_incoming_request_user_device)
|
||||
lateinit var otherDeviceTextView: TextView
|
||||
|
||||
@BindView(R.id.sas_incoming_request_user_avatar)
|
||||
lateinit var avatarImageView: ImageView
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_sas_verification_incoming_request
|
||||
|
||||
private lateinit var viewModel: SasVerificationViewModel
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
viewModel = activityViewModelProvider.get(SasVerificationViewModel::class.java)
|
||||
|
||||
otherUserDisplayNameTextView.text = viewModel.otherUser?.displayName ?: viewModel.otherUserId
|
||||
otherUserIdTextView.text = viewModel.otherUserId
|
||||
otherDeviceTextView.text = viewModel.otherDeviceId
|
||||
|
||||
viewModel.otherUser?.let {
|
||||
avatarRenderer.render(it.toMatrixItem(), avatarImageView)
|
||||
} ?: run {
|
||||
// Fallback to what we know
|
||||
avatarRenderer.render(MatrixItem.UserItem(viewModel.otherUserId ?: "", viewModel.otherUserId), avatarImageView)
|
||||
}
|
||||
|
||||
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
|
||||
val uxState = (viewModel.transaction as? IncomingSasVerificationTransaction)?.uxState
|
||||
when (uxState) {
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
|
||||
viewModel.loadingLiveEvent.value = null
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT -> {
|
||||
viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||
viewModel.shortCodeReady()
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME,
|
||||
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> {
|
||||
viewModel.loadingLiveEvent.value = null
|
||||
viewModel.navigateCancel()
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_request_continue_button)
|
||||
fun didAccept() {
|
||||
viewModel.acceptTransaction()
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_request_cancel_button)
|
||||
fun didCancel() {
|
||||
viewModel.cancelTransaction()
|
||||
}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Observer
|
||||
import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO Deprecated("replaced by bottomsheet UX")
|
||||
class SASVerificationShortCodeFragment @Inject constructor(): VectorBaseFragment() {
|
||||
|
||||
private lateinit var viewModel: SasVerificationViewModel
|
||||
|
||||
@BindView(R.id.sas_decimal_code)
|
||||
lateinit var decimalTextView: TextView
|
||||
|
||||
@BindView(R.id.sas_emoji_description)
|
||||
lateinit var descriptionTextView: TextView
|
||||
|
||||
@BindView(R.id.sas_emoji_grid)
|
||||
lateinit var emojiGrid: ViewGroup
|
||||
|
||||
@BindView(R.id.emoji0)
|
||||
lateinit var emoji0View: ViewGroup
|
||||
@BindView(R.id.emoji1)
|
||||
lateinit var emoji1View: ViewGroup
|
||||
@BindView(R.id.emoji2)
|
||||
lateinit var emoji2View: ViewGroup
|
||||
@BindView(R.id.emoji3)
|
||||
lateinit var emoji3View: ViewGroup
|
||||
@BindView(R.id.emoji4)
|
||||
lateinit var emoji4View: ViewGroup
|
||||
@BindView(R.id.emoji5)
|
||||
lateinit var emoji5View: ViewGroup
|
||||
@BindView(R.id.emoji6)
|
||||
lateinit var emoji6View: ViewGroup
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_sas_verification_display_code
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
viewModel = activityViewModelProvider.get(SasVerificationViewModel::class.java)
|
||||
|
||||
viewModel.transaction?.let {
|
||||
if (it.supportsEmoji()) {
|
||||
val emojicodes = it.getEmojiCodeRepresentation()
|
||||
emojicodes.forEachIndexed { index, emojiRepresentation ->
|
||||
when (index) {
|
||||
0 -> {
|
||||
emoji0View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji0View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
1 -> {
|
||||
emoji1View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji1View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
2 -> {
|
||||
emoji2View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji2View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
3 -> {
|
||||
emoji3View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji3View.findViewById<TextView>(R.id.item_emoji_name_tv)?.setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
4 -> {
|
||||
emoji4View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji4View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
5 -> {
|
||||
emoji5View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji5View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
6 -> {
|
||||
emoji6View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
|
||||
emoji6View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decimal is at least supported
|
||||
decimalTextView.text = it.getDecimalCodeRepresentation()
|
||||
|
||||
if (it.supportsEmoji()) {
|
||||
descriptionTextView.text = getString(R.string.sas_emoji_description)
|
||||
decimalTextView.isVisible = false
|
||||
emojiGrid.isVisible = true
|
||||
} else {
|
||||
descriptionTextView.text = getString(R.string.sas_decimal_description)
|
||||
decimalTextView.isVisible = true
|
||||
emojiGrid.isInvisible = true
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
|
||||
if (viewModel.transaction is IncomingSasVerificationTransaction) {
|
||||
val uxState = (viewModel.transaction as IncomingSasVerificationTransaction).uxState
|
||||
when (uxState) {
|
||||
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
|
||||
viewModel.loadingLiveEvent.value = null
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||
viewModel.loadingLiveEvent.value = null
|
||||
viewModel.deviceIsVerified()
|
||||
}
|
||||
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME,
|
||||
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> {
|
||||
viewModel.loadingLiveEvent.value = null
|
||||
viewModel.navigateCancel()
|
||||
}
|
||||
else -> {
|
||||
viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner
|
||||
}
|
||||
}
|
||||
} else if (viewModel.transaction is OutgoingSasVerificationRequest) {
|
||||
val uxState = (viewModel.transaction as OutgoingSasVerificationRequest).uxState
|
||||
when (uxState) {
|
||||
OutgoingSasVerificationRequest.UxState.SHOW_SAS -> {
|
||||
viewModel.loadingLiveEvent.value = null
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.VERIFIED -> {
|
||||
viewModel.loadingLiveEvent.value = null
|
||||
viewModel.deviceIsVerified()
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
|
||||
viewModel.loadingLiveEvent.value = null
|
||||
viewModel.navigateCancel()
|
||||
}
|
||||
else -> {
|
||||
viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_request_continue_button)
|
||||
fun didAccept() {
|
||||
viewModel.confirmEmojiSame()
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_request_cancel_button)
|
||||
fun didCancel() {
|
||||
viewModel.cancelTransaction()
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.transition.TransitionManager
|
||||
import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO Deprecated("replaced by bottomsheet UX")
|
||||
class SASVerificationStartFragment @Inject constructor(): VectorBaseFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_sas_verification_start
|
||||
|
||||
private lateinit var viewModel: SasVerificationViewModel
|
||||
|
||||
@BindView(R.id.rootLayout)
|
||||
lateinit var rootLayout: ViewGroup
|
||||
|
||||
@BindView(R.id.sas_start_button)
|
||||
lateinit var startButton: Button
|
||||
|
||||
@BindView(R.id.sas_start_button_loading)
|
||||
lateinit var startButtonLoading: ProgressBar
|
||||
|
||||
@BindView(R.id.sas_verifying_keys)
|
||||
lateinit var loadingText: TextView
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
viewModel = activityViewModelProvider.get(SasVerificationViewModel::class.java)
|
||||
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
|
||||
val uxState = (viewModel.transaction as? OutgoingSasVerificationRequest)?.uxState
|
||||
when (uxState) {
|
||||
OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> {
|
||||
// display loading
|
||||
TransitionManager.beginDelayedTransition(this.rootLayout)
|
||||
this.loadingText.isVisible = true
|
||||
this.startButton.isInvisible = true
|
||||
this.startButtonLoading.isVisible = true
|
||||
this.startButtonLoading.animate()
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.SHOW_SAS -> {
|
||||
viewModel.shortCodeReady()
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
|
||||
viewModel.navigateCancel()
|
||||
}
|
||||
else -> {
|
||||
TransitionManager.beginDelayedTransition(this.rootLayout)
|
||||
this.loadingText.isVisible = false
|
||||
this.startButton.isVisible = true
|
||||
this.startButtonLoading.isVisible = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_start_button)
|
||||
fun doStart() {
|
||||
viewModel.beginSasKeyVerification()
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_legacy_verification)
|
||||
fun doLegacy() {
|
||||
(requireActivity() as VectorBaseActivity).notImplemented()
|
||||
|
||||
/*
|
||||
viewModel.session.crypto?.getDeviceInfo(viewModel.otherUserMxItem ?: "", viewModel.otherDeviceId
|
||||
?: "", object : SimpleApiCallback<MXDeviceInfo>() {
|
||||
override fun onSuccess(info: MXDeviceInfo?) {
|
||||
info?.let {
|
||||
|
||||
CommonActivityUtils.displayDeviceVerificationDialogLegacy(it, it.userId, viewModel.session, activity, object : YesNoListener {
|
||||
override fun yes() {
|
||||
viewModel.manuallyVerified()
|
||||
}
|
||||
|
||||
override fun no() {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
*/
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_cancel_button)
|
||||
fun doCancel() {
|
||||
// Transaction may be started, or not
|
||||
viewModel.cancelTransaction()
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.os.Bundle
|
||||
import butterknife.OnClick
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO Deprecated("replaced by bottomsheet UX")
|
||||
class SASVerificationVerifiedFragment @Inject constructor() : VectorBaseFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_sas_verification_verified
|
||||
|
||||
private lateinit var viewModel: SasVerificationViewModel
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
viewModel = activityViewModelProvider.get(SasVerificationViewModel::class.java)
|
||||
}
|
||||
|
||||
@OnClick(R.id.sas_verification_verified_done_button)
|
||||
fun onDone() {
|
||||
viewModel.finishSuccess()
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotx.core.utils.LiveEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO Deprecated("replaced by bottomsheet UX")
|
||||
class SasVerificationViewModel @Inject constructor() : ViewModel(),
|
||||
SasVerificationService.SasVerificationListener {
|
||||
|
||||
companion object {
|
||||
const val NAVIGATE_FINISH = "NAVIGATE_FINISH"
|
||||
const val NAVIGATE_FINISH_SUCCESS = "NAVIGATE_FINISH_SUCCESS"
|
||||
const val NAVIGATE_SAS_DISPLAY = "NAVIGATE_SAS_DISPLAY"
|
||||
const val NAVIGATE_SUCCESS = "NAVIGATE_SUCCESS"
|
||||
const val NAVIGATE_CANCELLED = "NAVIGATE_CANCELLED"
|
||||
}
|
||||
|
||||
private lateinit var sasVerificationService: SasVerificationService
|
||||
|
||||
var otherUserId: String? = null
|
||||
var otherDeviceId: String? = null
|
||||
var otherUser: User? = null
|
||||
var transaction: SasVerificationTransaction? = null
|
||||
|
||||
var transactionState: MutableLiveData<SasVerificationTxState> = MutableLiveData()
|
||||
|
||||
init {
|
||||
// Force a first observe
|
||||
transactionState.value = null
|
||||
}
|
||||
|
||||
private var _navigateEvent: MutableLiveData<LiveEvent<String>> = MutableLiveData()
|
||||
val navigateEvent: LiveData<LiveEvent<String>>
|
||||
get() = _navigateEvent
|
||||
|
||||
var loadingLiveEvent: MutableLiveData<Int> = MutableLiveData()
|
||||
|
||||
var transactionID: String? = null
|
||||
set(value) {
|
||||
if (value != null) {
|
||||
transaction = sasVerificationService.getExistingTransaction(otherUserId!!, value)
|
||||
transactionState.value = transaction?.state
|
||||
otherDeviceId = transaction?.otherDeviceId
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
fun initIncoming(session: Session, otherUserId: String, transactionID: String?) {
|
||||
this.sasVerificationService = session.getSasVerificationService()
|
||||
this.otherUserId = otherUserId
|
||||
this.transactionID = transactionID
|
||||
this.sasVerificationService.addListener(this)
|
||||
this.otherUser = session.getUser(otherUserId)
|
||||
if (transactionID == null || transaction == null) {
|
||||
// sanity, this transaction is not known anymore
|
||||
_navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
|
||||
}
|
||||
}
|
||||
|
||||
fun initOutgoing(session: Session, otherUserId: String, otherDeviceId: String) {
|
||||
this.sasVerificationService = session.getSasVerificationService()
|
||||
this.otherUserId = otherUserId
|
||||
this.otherDeviceId = otherDeviceId
|
||||
this.sasVerificationService.addListener(this)
|
||||
this.otherUser = session.getUser(otherUserId)
|
||||
}
|
||||
|
||||
fun beginSasKeyVerification() {
|
||||
val verificationSAS = sasVerificationService.beginKeyVerificationSAS(otherUserId!!, otherDeviceId!!)
|
||||
this.transactionID = verificationSAS
|
||||
}
|
||||
|
||||
override fun transactionCreated(tx: SasVerificationTransaction) {
|
||||
}
|
||||
|
||||
override fun transactionUpdated(tx: SasVerificationTransaction) {
|
||||
if (transactionID == tx.transactionId) {
|
||||
transactionState.value = tx.state
|
||||
}
|
||||
}
|
||||
|
||||
override fun markedAsManuallyVerified(userId: String, deviceId: String) {
|
||||
}
|
||||
|
||||
fun cancelTransaction() {
|
||||
transaction?.cancel()
|
||||
_navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
|
||||
}
|
||||
|
||||
fun finishSuccess() {
|
||||
_navigateEvent.value = LiveEvent(NAVIGATE_FINISH_SUCCESS)
|
||||
}
|
||||
|
||||
fun manuallyVerified() {
|
||||
if (otherUserId != null && otherDeviceId != null) {
|
||||
sasVerificationService.markedLocallyAsManuallyVerified(otherUserId!!, otherDeviceId!!)
|
||||
}
|
||||
_navigateEvent.value = LiveEvent(NAVIGATE_FINISH_SUCCESS)
|
||||
}
|
||||
|
||||
fun acceptTransaction() {
|
||||
(transaction as? IncomingSasVerificationTransaction)?.performAccept()
|
||||
}
|
||||
|
||||
fun confirmEmojiSame() {
|
||||
transaction?.userHasVerifiedShortCode()
|
||||
}
|
||||
|
||||
fun shortCodeReady() {
|
||||
loadingLiveEvent.value = null
|
||||
_navigateEvent.value = LiveEvent(NAVIGATE_SAS_DISPLAY)
|
||||
}
|
||||
|
||||
fun deviceIsVerified() {
|
||||
loadingLiveEvent.value = null
|
||||
_navigateEvent.value = LiveEvent(NAVIGATE_SUCCESS)
|
||||
}
|
||||
|
||||
fun navigateCancel() {
|
||||
_navigateEvent.value = LiveEvent(NAVIGATE_CANCELLED)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
if (::sasVerificationService.isInitialized) {
|
||||
sasVerificationService.removeListener(this)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,20 +17,16 @@ package im.vector.riotx.features.crypto.verification
|
|||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.transition.AutoTransition
|
||||
import androidx.transition.TransitionManager
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.Unbinder
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
|
@ -40,9 +36,12 @@ import im.vector.riotx.R
|
|||
import im.vector.riotx.core.di.ScreenComponent
|
||||
import im.vector.riotx.core.extensions.commitTransactionNow
|
||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import im.vector.riotx.core.utils.colorizeMatchingText
|
||||
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||
import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
|
||||
import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment
|
||||
import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
|
||||
import im.vector.riotx.features.crypto.verification.request.VerificationRequestViewModel
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_verification.*
|
||||
import timber.log.Timber
|
||||
|
@ -74,22 +73,13 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
@BindView(R.id.verificationRequestName)
|
||||
lateinit var otherUserNameText: TextView
|
||||
|
||||
@BindView(R.id.verificationRequestShield)
|
||||
lateinit var otherUserShield: View
|
||||
|
||||
@BindView(R.id.verificationRequestAvatar)
|
||||
lateinit var otherUserAvatarImageView: ImageView
|
||||
|
||||
private var unBinder: Unbinder? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.bottom_sheet_verification, container, false)
|
||||
unBinder = ButterKnife.bind(this, view)
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
unBinder?.unbind()
|
||||
unBinder = null
|
||||
}
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -109,12 +99,15 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
it.otherUserMxItem?.let { matrixItem ->
|
||||
val displayName = matrixItem.displayName ?: ""
|
||||
otherUserNameText.text = getString(R.string.verification_request_alert_title, displayName)
|
||||
.toSpannable()
|
||||
.colorizeMatchingText(displayName, ThemeUtils.getColor(requireContext(), R.attr.vctr_notice_text_color))
|
||||
|
||||
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
||||
|
||||
if(it.sasTransactionState == SasVerificationTxState.Verified) {
|
||||
otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName())
|
||||
otherUserShield.isVisible = true
|
||||
} else {
|
||||
otherUserNameText.text = getString(R.string.verification_verify_user, matrixItem.getBestName())
|
||||
otherUserShield.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
// Did the request result in a SAS transaction?
|
||||
|
@ -135,7 +128,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
SasVerificationTxState.SendingMac,
|
||||
SasVerificationTxState.MacSent,
|
||||
SasVerificationTxState.Verifying -> {
|
||||
showFragment(SASVerificationCodeFragment::class, Bundle().apply {
|
||||
showFragment(VerificationEmojiCodeFragment::class, Bundle().apply {
|
||||
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
||||
it.otherUserMxItem?.id ?: "",
|
||||
it.pendingRequest?.transactionId))
|
||||
|
|
|
@ -21,13 +21,9 @@ import com.airbnb.mvrx.*
|
|||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.api.session.crypto.sas.*
|
||||
import im.vector.matrix.android.api.util.MatrixItem
|
||||
import im.vector.matrix.android.api.util.toMatrixItem
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
|
||||
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
|
||||
import im.vector.riotx.core.di.HasScreenInjector
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
|
@ -108,7 +104,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
is VerificationAction.RequestVerificationByDM -> {
|
||||
// session
|
||||
setState {
|
||||
copy(pendingRequest = session.getSasVerificationService().requestKeyVerificationInDMs(otherUserId, roomId))
|
||||
copy(pendingRequest = session.getSasVerificationService().requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, roomId))
|
||||
}
|
||||
}
|
||||
is VerificationAction.StartSASVerification -> {
|
||||
|
@ -117,7 +113,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
|
||||
val otherDevice = if (request.isIncoming) request.requestInfo?.fromDevice else request.readyInfo?.fromDevice
|
||||
session.getSasVerificationService().beginKeyVerificationInDMs(
|
||||
KeyVerificationStart.VERIF_METHOD_SAS,
|
||||
VerificationMethod.SAS,
|
||||
transactionId = action.pendingRequestTransactionId,
|
||||
roomId = roomId,
|
||||
otherUserId = request.otherUserId,
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.View
|
||||
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.utils.tappableMatchingText
|
||||
import kotlinx.android.synthetic.main.fragment_verification_choose_method.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationChooseMethodFragment @Inject constructor(
|
||||
val verificationChooseMethodViewModelFactory: VerificationChooseMethodViewModel.Factory
|
||||
) : VectorBaseFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_verification_choose_method
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationChooseMethodViewModel::class)
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
if (state.QRModeAvailable) {
|
||||
val cSpan = object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
}
|
||||
}
|
||||
val openLink = getString(R.string.verify_open_camera_link)
|
||||
val descCharSequence =
|
||||
getString(R.string.verify_by_scanning_description, openLink)
|
||||
.toSpannable()
|
||||
.tappableMatchingText(openLink, cSpan)
|
||||
verifyQRDescription.text = descCharSequence
|
||||
verifyQRGroup.isVisible = true
|
||||
} else {
|
||||
verifyQRGroup.isVisible = false
|
||||
}
|
||||
|
||||
verifyEmojiGroup.isVisible = state.SASMOdeAvailable
|
||||
}
|
||||
|
||||
@OnClick(R.id.verificationByEmojiButton)
|
||||
fun doVerifyBySas() = withState(sharedViewModel) {
|
||||
sharedViewModel.handle(VerificationAction.StartSASVerification(it.otherUserMxItem?.id ?: "", it.pendingRequest?.transactionId
|
||||
?: ""))
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.core.content.ContextCompat
|
||||
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.extensions.setTextOrHide
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_verification_conclusion.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationConclusionFragment @Inject constructor() : VectorBaseFragment() {
|
||||
|
||||
@Parcelize
|
||||
data class Args(
|
||||
val isSuccessFull: Boolean,
|
||||
val cancelReason: String?
|
||||
) : Parcelable
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_verification_conclusion
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationConclusionViewModel::class)
|
||||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
when (it.conclusionState) {
|
||||
ConclusionState.SUCCESS -> {
|
||||
verificationConclusionTitle.text = getString(R.string.sas_verified)
|
||||
verifyConclusionDescription.setTextOrHide(getString(R.string.sas_verified_successful_description))
|
||||
verifyConclusionBottomDescription.text = getString(R.string.verification_green_shield)
|
||||
verifyConclusionImageView.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_shield_trusted))
|
||||
}
|
||||
ConclusionState.WARNING -> {
|
||||
verificationConclusionTitle.text = getString(R.string.verification_conclusion_not_secure)
|
||||
verifyConclusionDescription.isVisible = false
|
||||
verifyConclusionImageView.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_shield_warning))
|
||||
|
||||
verifyConclusionBottomDescription.text = Markwon.builder(requireContext())
|
||||
.build()
|
||||
.toMarkdown(getString(R.string.verification_conclusion_compromised))
|
||||
}
|
||||
ConclusionState.CANCELLED -> {
|
||||
// Just dismiss in this case
|
||||
sharedViewModel.handle(VerificationAction.GotItConclusion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.verificationConclusionButton)
|
||||
fun onButtonTapped() {
|
||||
sharedViewModel.handle(VerificationAction.GotItConclusion)
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification
|
||||
|
||||
import android.graphics.Typeface
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.core.view.isInvisible
|
||||
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.utils.colorizeMatchingText
|
||||
import im.vector.riotx.core.utils.styleMatchingText
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import kotlinx.android.synthetic.main.fragment_verification_request.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationRequestFragment @Inject constructor(
|
||||
val verificationRequestViewModelFactory: VerificationRequestViewModel.Factory,
|
||||
val avatarRenderer: AvatarRenderer
|
||||
) : VectorBaseFragment() {
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationRequestViewModel::class)
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_verification_request
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
state.matrixItem.let {
|
||||
val styledText = getString(R.string.verification_request_alert_description, it.id)
|
||||
.toSpannable()
|
||||
.styleMatchingText(it.id, Typeface.BOLD)
|
||||
.colorizeMatchingText(it.id, ThemeUtils.getColor(requireContext(), R.attr.vctr_notice_text_color))
|
||||
verificationRequestText.text = styledText
|
||||
}
|
||||
|
||||
when (state.started) {
|
||||
is Loading -> {
|
||||
// Hide the start button, show waiting
|
||||
verificationStartButton.isInvisible = true
|
||||
verificationWaitingText.isVisible = true
|
||||
val otherUser = state.matrixItem.displayName ?: state.matrixItem.id
|
||||
verificationWaitingText.text = getString(R.string.verification_request_waiting_for, otherUser)
|
||||
.toSpannable()
|
||||
.styleMatchingText(otherUser, Typeface.BOLD)
|
||||
.colorizeMatchingText(otherUser, ThemeUtils.getColor(requireContext(), R.attr.vctr_notice_text_color))
|
||||
}
|
||||
else -> {
|
||||
verificationStartButton.isEnabled = true
|
||||
verificationStartButton.isVisible = true
|
||||
verificationWaitingText.isInvisible = true
|
||||
}
|
||||
}
|
||||
|
||||
Unit
|
||||
}
|
||||
|
||||
@OnClick(R.id.verificationStartButton)
|
||||
fun onClickOnVerificationStart() = withState(viewModel) { state ->
|
||||
verificationStartButton.isEnabled = false
|
||||
sharedViewModel.handle(VerificationAction.RequestVerificationByDM(state.matrixItem.id, state.roomId))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.crypto.verification.choose
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.dividerItem
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationChooseMethodController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider
|
||||
) : EpoxyController() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
private var viewState: VerificationChooseMethodViewState? = null
|
||||
|
||||
fun update(viewState: VerificationChooseMethodViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val state = viewState ?: return
|
||||
|
||||
if (state.QRModeAvailable) {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verification_scan_notice))
|
||||
}
|
||||
|
||||
// TODO Generate the QR code
|
||||
bottomSheetVerificationBigImageItem {
|
||||
id("qr")
|
||||
imageRes(R.drawable.riotx_logo)
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("sep0")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("openCamera")
|
||||
title(stringProvider.getString(R.string.verification_scan_their_code))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_accent))
|
||||
iconRes(R.drawable.ic_camera)
|
||||
iconColor(colorProvider.getColor(R.color.riotx_accent))
|
||||
listener { listener?.openCamera() }
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("sep1")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("openEmoji")
|
||||
title(stringProvider.getString(R.string.verification_scan_emoji_title))
|
||||
titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
subTitle(stringProvider.getString(R.string.verification_scan_emoji_subtitle))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.doVerifyBySas() }
|
||||
}
|
||||
} else if (state.SASModeAvailable) {
|
||||
bottomSheetVerificationActionItem {
|
||||
id("openEmoji")
|
||||
title(stringProvider.getString(R.string.verification_no_scan_emoji_title))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_accent))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.doVerifyBySas() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun openCamera()
|
||||
fun doVerifyBySas()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.choose
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
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.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.crypto.verification.VerificationAction
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationChooseMethodFragment @Inject constructor(
|
||||
val verificationChooseMethodViewModelFactory: VerificationChooseMethodViewModel.Factory,
|
||||
val controller: VerificationChooseMethodController
|
||||
) : VectorBaseFragment(), VerificationChooseMethodController.Listener {
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationChooseMethodViewModel::class)
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
bottomSheetVerificationRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
controller.update(state)
|
||||
}
|
||||
|
||||
override fun doVerifyBySas() = withState(sharedViewModel) {
|
||||
sharedViewModel.handle(VerificationAction.StartSASVerification(
|
||||
it.otherUserMxItem?.id ?: "",
|
||||
it.pendingRequest?.transactionId ?: ""))
|
||||
}
|
||||
|
||||
override fun openCamera() {
|
||||
// TODO
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification
|
||||
package im.vector.riotx.features.crypto.verification.choose
|
||||
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
|
@ -24,17 +24,18 @@ import com.squareup.inject.assisted.AssistedInject
|
|||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
||||
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
|
||||
import im.vector.riotx.core.di.HasScreenInjector
|
||||
import im.vector.riotx.core.platform.EmptyAction
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
|
||||
|
||||
data class VerificationChooseMethodViewState(
|
||||
val otherUserId: String = "",
|
||||
val transactionId: String = "",
|
||||
val QRModeAvailable: Boolean = false,
|
||||
val SASMOdeAvailable: Boolean = false
|
||||
val SASModeAvailable: Boolean = false
|
||||
) : MvRxState
|
||||
|
||||
class VerificationChooseMethodViewModel @AssistedInject constructor(
|
||||
|
@ -48,15 +49,13 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
|
|||
|
||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
|
||||
val pvr = session.getSasVerificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
|
||||
val qrAvailable = pvr?.readyInfo?.methods?.contains(KeyVerificationStart.VERIF_METHOD_SCAN)
|
||||
?: false
|
||||
val emojiAvailable = pvr?.readyInfo?.methods?.contains(KeyVerificationStart.VERIF_METHOD_SAS)
|
||||
?: false
|
||||
val qrAvailable = pvr?.hasMethod(VerificationMethod.SCAN) ?: false
|
||||
val emojiAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false
|
||||
|
||||
setState {
|
||||
copy(
|
||||
QRModeAvailable = qrAvailable,
|
||||
SASMOdeAvailable = emojiAvailable
|
||||
SASModeAvailable = emojiAvailable
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -85,15 +84,13 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
|
|||
val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args()
|
||||
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
|
||||
val pvr = session.getSasVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
|
||||
val qrAvailable = pvr?.readyInfo?.methods?.contains(KeyVerificationStart.VERIF_METHOD_SCAN)
|
||||
?: false
|
||||
val emojiAvailable = pvr?.readyInfo?.methods?.contains(KeyVerificationStart.VERIF_METHOD_SAS)
|
||||
?: false
|
||||
val qrAvailable = pvr?.hasMethod(VerificationMethod.SCAN) ?: false
|
||||
val emojiAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false
|
||||
|
||||
return VerificationChooseMethodViewState(otherUserId = args.otherUserId,
|
||||
transactionId = args.verificationId ?: "",
|
||||
QRModeAvailable = qrAvailable,
|
||||
SASMOdeAvailable = emojiAvailable
|
||||
SASModeAvailable = emojiAvailable
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.crypto.verification.conclusion
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.dividerItem
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationConclusionController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider,
|
||||
private val eventHtmlRenderer: EventHtmlRenderer
|
||||
) : EpoxyController() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
private var viewState: VerificationConclusionViewState? = null
|
||||
|
||||
fun update(viewState: VerificationConclusionViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val state = viewState ?: return
|
||||
|
||||
when (state.conclusionState) {
|
||||
ConclusionState.SUCCESS -> {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verification_conclusion_ok_notice))
|
||||
}
|
||||
|
||||
bottomSheetVerificationBigImageItem {
|
||||
id("image")
|
||||
imageRes(R.drawable.ic_shield_trusted)
|
||||
}
|
||||
}
|
||||
ConclusionState.WARNING -> {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verification_conclusion_not_secure))
|
||||
}
|
||||
|
||||
bottomSheetVerificationBigImageItem {
|
||||
id("image")
|
||||
imageRes(R.drawable.ic_shield_warning)
|
||||
}
|
||||
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("warning_notice")
|
||||
notice(eventHtmlRenderer.render(stringProvider.getString(R.string.verification_conclusion_compromised)))
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("sep0")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("done")
|
||||
title(stringProvider.getString(R.string.done))
|
||||
titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.onButtonTapped() }
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onButtonTapped()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.conclusion
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.View
|
||||
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.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.crypto.verification.VerificationAction
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationConclusionFragment @Inject constructor(
|
||||
val controller: VerificationConclusionController
|
||||
) : VectorBaseFragment(), VerificationConclusionController.Listener {
|
||||
|
||||
@Parcelize
|
||||
data class Args(
|
||||
val isSuccessFull: Boolean,
|
||||
val cancelReason: String?
|
||||
) : Parcelable
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationConclusionViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
bottomSheetVerificationRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
if (state.conclusionState == ConclusionState.CANCELLED) {
|
||||
// Just dismiss in this case
|
||||
sharedViewModel.handle(VerificationAction.GotItConclusion)
|
||||
} else {
|
||||
controller.update(state)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onButtonTapped() {
|
||||
sharedViewModel.handle(VerificationAction.GotItConclusion)
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification
|
||||
package im.vector.riotx.features.crypto.verification.conclusion
|
||||
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
|
@ -23,7 +23,7 @@ import im.vector.matrix.android.api.session.crypto.sas.safeValueOf
|
|||
import im.vector.riotx.core.platform.EmptyAction
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
|
||||
data class SASVerificationConclusionViewState(
|
||||
data class VerificationConclusionViewState(
|
||||
val conclusionState: ConclusionState = ConclusionState.CANCELLED
|
||||
) : MvRxState
|
||||
|
||||
|
@ -33,22 +33,22 @@ enum class ConclusionState {
|
|||
CANCELLED
|
||||
}
|
||||
|
||||
class VerificationConclusionViewModel(initialState: SASVerificationConclusionViewState)
|
||||
: VectorViewModel<SASVerificationConclusionViewState, EmptyAction>(initialState) {
|
||||
class VerificationConclusionViewModel(initialState: VerificationConclusionViewState)
|
||||
: VectorViewModel<VerificationConclusionViewState, EmptyAction>(initialState) {
|
||||
|
||||
companion object : MvRxViewModelFactory<VerificationConclusionViewModel, SASVerificationConclusionViewState> {
|
||||
companion object : MvRxViewModelFactory<VerificationConclusionViewModel, VerificationConclusionViewState> {
|
||||
|
||||
override fun initialState(viewModelContext: ViewModelContext): SASVerificationConclusionViewState? {
|
||||
override fun initialState(viewModelContext: ViewModelContext): VerificationConclusionViewState? {
|
||||
val args = viewModelContext.args<VerificationConclusionFragment.Args>()
|
||||
|
||||
return when (safeValueOf(args.cancelReason)) {
|
||||
CancelCode.MismatchedSas,
|
||||
CancelCode.MismatchedCommitment,
|
||||
CancelCode.MismatchedKeys -> {
|
||||
SASVerificationConclusionViewState(ConclusionState.WARNING)
|
||||
VerificationConclusionViewState(ConclusionState.WARNING)
|
||||
}
|
||||
else -> {
|
||||
SASVerificationConclusionViewState(
|
||||
VerificationConclusionViewState(
|
||||
if (args.isSuccessFull) ConclusionState.SUCCESS
|
||||
else ConclusionState.CANCELLED
|
||||
)
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.crypto.verification.emoji
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.dividerItem
|
||||
import im.vector.riotx.core.epoxy.errorWithRetryItem
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationDecimalCodeItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationEmojisItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationEmojiCodeController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider,
|
||||
private val errorFormatter: ErrorFormatter
|
||||
) : EpoxyController() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
private var viewState: VerificationEmojiCodeViewState? = null
|
||||
|
||||
fun update(viewState: VerificationEmojiCodeViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val state = viewState ?: return
|
||||
|
||||
if (state.supportsEmoji) {
|
||||
buildEmojiItem(state)
|
||||
} else {
|
||||
buildDecimal(state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildEmojiItem(state: VerificationEmojiCodeViewState) {
|
||||
when (val emojiDescription = state.emojiDescription) {
|
||||
is Success -> {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verification_emoji_notice))
|
||||
}
|
||||
|
||||
bottomSheetVerificationEmojisItem {
|
||||
id("emojis")
|
||||
emojiRepresentation0(emojiDescription()[0])
|
||||
emojiRepresentation1(emojiDescription()[1])
|
||||
emojiRepresentation2(emojiDescription()[2])
|
||||
emojiRepresentation3(emojiDescription()[3])
|
||||
emojiRepresentation4(emojiDescription()[4])
|
||||
emojiRepresentation5(emojiDescription()[5])
|
||||
emojiRepresentation6(emojiDescription()[6])
|
||||
}
|
||||
|
||||
buildActions(state)
|
||||
}
|
||||
is Fail -> {
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
text(errorFormatter.toHumanReadable(emojiDescription.error))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(stringProvider.getString(R.string.please_wait))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDecimal(state: VerificationEmojiCodeViewState) {
|
||||
when (val decimalDescription = state.decimalDescription) {
|
||||
is Success -> {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verification_code_notice))
|
||||
}
|
||||
|
||||
bottomSheetVerificationDecimalCodeItem {
|
||||
id("decimal")
|
||||
code(state.decimalDescription.invoke() ?: "")
|
||||
}
|
||||
|
||||
buildActions(state)
|
||||
}
|
||||
is Fail -> {
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
text(errorFormatter.toHumanReadable(decimalDescription.error))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(stringProvider.getString(R.string.please_wait))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildActions(state: VerificationEmojiCodeViewState) {
|
||||
dividerItem {
|
||||
id("sep0")
|
||||
}
|
||||
|
||||
if (state.isWaitingFromOther) {
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(stringProvider.getString(R.string.verification_request_waiting_for, state.otherUser?.getBestName() ?: ""))
|
||||
}
|
||||
} else {
|
||||
bottomSheetVerificationActionItem {
|
||||
id("ko")
|
||||
title(stringProvider.getString(R.string.verification_sas_do_not_match))
|
||||
titleColor(colorProvider.getColor(R.color.vector_error_color))
|
||||
iconRes(R.drawable.ic_check_off)
|
||||
iconColor(colorProvider.getColor(R.color.vector_error_color))
|
||||
listener { listener?.onDoNotMatchButtonTapped() }
|
||||
}
|
||||
dividerItem {
|
||||
id("sep1")
|
||||
}
|
||||
bottomSheetVerificationActionItem {
|
||||
id("ok")
|
||||
title(stringProvider.getString(R.string.verification_sas_match))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_accent))
|
||||
iconRes(R.drawable.ic_check_on)
|
||||
iconColor(colorProvider.getColor(R.color.riotx_accent))
|
||||
listener { listener?.onMatchButtonTapped() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onDoNotMatchButtonTapped()
|
||||
fun onMatchButtonTapped()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.emoji
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
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.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.crypto.verification.VerificationAction
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationEmojiCodeFragment @Inject constructor(
|
||||
val viewModelFactory: VerificationEmojiCodeViewModel.Factory,
|
||||
val controller: VerificationEmojiCodeController
|
||||
) : VectorBaseFragment(), VerificationEmojiCodeController.Listener {
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationEmojiCodeViewModel::class)
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
bottomSheetVerificationRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
controller.update(state)
|
||||
}
|
||||
|
||||
override fun onMatchButtonTapped() = withState(viewModel) { state ->
|
||||
val otherUserId = state.otherUser?.id ?: return@withState
|
||||
val txId = state.transactionId ?: return@withState
|
||||
sharedViewModel.handle(VerificationAction.SASMatchAction(otherUserId, txId))
|
||||
}
|
||||
|
||||
override fun onDoNotMatchButtonTapped() = withState(viewModel) { state ->
|
||||
val otherUserId = state.otherUser?.id ?: return@withState
|
||||
val txId = state.transactionId ?: return@withState
|
||||
sharedViewModel.handle(VerificationAction.SASDoNotMatchAction(otherUserId, txId))
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification
|
||||
package im.vector.riotx.features.crypto.verification.emoji
|
||||
|
||||
import com.airbnb.mvrx.*
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
|
@ -28,8 +28,9 @@ import im.vector.matrix.android.api.util.toMatrixItem
|
|||
import im.vector.riotx.core.di.HasScreenInjector
|
||||
import im.vector.riotx.core.platform.EmptyAction
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
|
||||
|
||||
data class SASVerificationCodeViewState(
|
||||
data class VerificationEmojiCodeViewState(
|
||||
val transactionId: String?,
|
||||
val otherUser: MatrixItem? = null,
|
||||
val supportsEmoji: Boolean = true,
|
||||
|
@ -38,10 +39,10 @@ data class SASVerificationCodeViewState(
|
|||
val isWaitingFromOther: Boolean = false
|
||||
) : MvRxState
|
||||
|
||||
class SASVerificationCodeViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: SASVerificationCodeViewState,
|
||||
class VerificationEmojiCodeViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: VerificationEmojiCodeViewState,
|
||||
private val session: Session
|
||||
) : VectorViewModel<SASVerificationCodeViewState, EmptyAction>(initialState), SasVerificationService.SasVerificationListener {
|
||||
) : VectorViewModel<VerificationEmojiCodeViewState, EmptyAction>(initialState), SasVerificationService.SasVerificationListener {
|
||||
|
||||
init {
|
||||
withState { state ->
|
||||
|
@ -141,22 +142,22 @@ class SASVerificationCodeViewModel @AssistedInject constructor(
|
|||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: SASVerificationCodeViewState): SASVerificationCodeViewModel
|
||||
fun create(initialState: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<SASVerificationCodeViewModel, SASVerificationCodeViewState> {
|
||||
companion object : MvRxViewModelFactory<VerificationEmojiCodeViewModel, VerificationEmojiCodeViewState> {
|
||||
|
||||
override fun create(viewModelContext: ViewModelContext, state: SASVerificationCodeViewState): SASVerificationCodeViewModel? {
|
||||
val factory = (viewModelContext as FragmentViewModelContext).fragment<SASVerificationCodeFragment>().viewModelFactory
|
||||
override fun create(viewModelContext: ViewModelContext, state: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel? {
|
||||
val factory = (viewModelContext as FragmentViewModelContext).fragment<VerificationEmojiCodeFragment>().viewModelFactory
|
||||
return factory.create(state)
|
||||
}
|
||||
|
||||
override fun initialState(viewModelContext: ViewModelContext): SASVerificationCodeViewState? {
|
||||
override fun initialState(viewModelContext: ViewModelContext): VerificationEmojiCodeViewState? {
|
||||
val args = viewModelContext.args<VerificationBottomSheet.VerificationArgs>()
|
||||
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
|
||||
val matrixItem = session.getUser(args.otherUserId)?.toMatrixItem()
|
||||
|
||||
return SASVerificationCodeViewState(
|
||||
return VerificationEmojiCodeViewState(
|
||||
transactionId = args.verificationId,
|
||||
otherUser = matrixItem
|
||||
)
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification.epoxy
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.ImageViewCompat
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotx.core.extensions.setTextOrHide
|
||||
|
||||
/**
|
||||
* A action for bottom sheet.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_verification_action)
|
||||
abstract class BottomSheetVerificationActionItem : VectorEpoxyModel<BottomSheetVerificationActionItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
@DrawableRes
|
||||
var iconRes: Int = -1
|
||||
@EpoxyAttribute
|
||||
var title: CharSequence = ""
|
||||
@EpoxyAttribute
|
||||
var subTitle: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var titleColor: Int = 0
|
||||
@EpoxyAttribute
|
||||
var iconColor: Int = -1
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var listener: () -> Unit
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.view.setOnClickListener {
|
||||
listener.invoke()
|
||||
}
|
||||
|
||||
holder.title.text = title
|
||||
holder.title.setTextColor(titleColor)
|
||||
|
||||
holder.subTitle.setTextOrHide(subTitle)
|
||||
|
||||
if (iconRes != -1) {
|
||||
holder.icon.isVisible = true
|
||||
holder.icon.setImageResource(iconRes)
|
||||
if (iconColor != -1) {
|
||||
ImageViewCompat.setImageTintList(holder.icon, ColorStateList.valueOf(iconColor))
|
||||
}
|
||||
} else {
|
||||
holder.icon.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val title by bind<TextView>(R.id.itemVerificationActionTitle)
|
||||
val subTitle by bind<TextView>(R.id.itemVerificationActionSubTitle)
|
||||
val icon by bind<ImageView>(R.id.itemVerificationActionIcon)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification.epoxy
|
||||
|
||||
import android.widget.ImageView
|
||||
import androidx.core.view.ViewCompat
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
|
||||
/**
|
||||
* A action for bottom sheet.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_verification_big_image)
|
||||
abstract class BottomSheetVerificationBigImageItem : VectorEpoxyModel<BottomSheetVerificationBigImageItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var imageRes: Int = 0
|
||||
|
||||
@EpoxyAttribute
|
||||
var contentDescription: String? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.image.setImageResource(imageRes)
|
||||
|
||||
if (contentDescription == null) {
|
||||
ViewCompat.setImportantForAccessibility(holder.image, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO)
|
||||
} else {
|
||||
ViewCompat.setImportantForAccessibility(holder.image, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES)
|
||||
holder.image.contentDescription = contentDescription
|
||||
}
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val image by bind<ImageView>(R.id.itemVerificationBigImage)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification.epoxy
|
||||
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
|
||||
/**
|
||||
* A action for bottom sheet.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_verification_decimal_code)
|
||||
abstract class BottomSheetVerificationDecimalCodeItem : VectorEpoxyModel<BottomSheetVerificationDecimalCodeItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var code: CharSequence = ""
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.code.text = code
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val code by bind<TextView>(R.id.itemVerificationDecimalCode)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification.epoxy
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
|
||||
/**
|
||||
* A emoji list for bottom sheet.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_verification_emojis)
|
||||
abstract class BottomSheetVerificationEmojisItem : VectorEpoxyModel<BottomSheetVerificationEmojisItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute lateinit var emojiRepresentation0: EmojiRepresentation
|
||||
@EpoxyAttribute lateinit var emojiRepresentation1: EmojiRepresentation
|
||||
@EpoxyAttribute lateinit var emojiRepresentation2: EmojiRepresentation
|
||||
@EpoxyAttribute lateinit var emojiRepresentation3: EmojiRepresentation
|
||||
@EpoxyAttribute lateinit var emojiRepresentation4: EmojiRepresentation
|
||||
@EpoxyAttribute lateinit var emojiRepresentation5: EmojiRepresentation
|
||||
@EpoxyAttribute lateinit var emojiRepresentation6: EmojiRepresentation
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.emoji0View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation0.emoji
|
||||
holder.emoji0View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation0.nameResId)
|
||||
|
||||
holder.emoji1View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation1.emoji
|
||||
holder.emoji1View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation1.nameResId)
|
||||
|
||||
holder.emoji2View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation2.emoji
|
||||
holder.emoji2View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation2.nameResId)
|
||||
|
||||
holder.emoji3View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation3.emoji
|
||||
holder.emoji3View.findViewById<TextView>(R.id.item_emoji_name_tv)?.setText(emojiRepresentation3.nameResId)
|
||||
|
||||
holder.emoji4View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation4.emoji
|
||||
holder.emoji4View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation4.nameResId)
|
||||
|
||||
holder.emoji5View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation5.emoji
|
||||
holder.emoji5View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation5.nameResId)
|
||||
|
||||
holder.emoji6View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation6.emoji
|
||||
holder.emoji6View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation6.nameResId)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val emoji0View by bind<ViewGroup>(R.id.emoji0)
|
||||
val emoji1View by bind<ViewGroup>(R.id.emoji1)
|
||||
val emoji2View by bind<ViewGroup>(R.id.emoji2)
|
||||
val emoji3View by bind<ViewGroup>(R.id.emoji3)
|
||||
val emoji4View by bind<ViewGroup>(R.id.emoji4)
|
||||
val emoji5View by bind<ViewGroup>(R.id.emoji5)
|
||||
val emoji6View by bind<ViewGroup>(R.id.emoji6)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification.epoxy
|
||||
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
|
||||
/**
|
||||
* A action for bottom sheet.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_verification_notice)
|
||||
abstract class BottomSheetVerificationNoticeItem : VectorEpoxyModel<BottomSheetVerificationNoticeItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var notice: CharSequence = ""
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.notice.text = notice
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val notice by bind<TextView>(R.id.itemVerificationNoticeText)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification.epoxy
|
||||
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
|
||||
/**
|
||||
* A action for bottom sheet.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_verification_waiting)
|
||||
abstract class BottomSheetVerificationWaitingItem : VectorEpoxyModel<BottomSheetVerificationWaitingItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var title: CharSequence = ""
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.title.text = title
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val title by bind<TextView>(R.id.itemVerificationWaitingTitle)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.request
|
||||
|
||||
import androidx.core.text.toSpannable
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import com.airbnb.mvrx.Loading
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.dividerItem
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.core.utils.colorizeMatchingText
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationRequestController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider
|
||||
) : EpoxyController() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
private var viewState: VerificationRequestViewState? = null
|
||||
|
||||
fun update(viewState: VerificationRequestViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val state = viewState ?: return
|
||||
|
||||
val styledText = state.matrixItem.let {
|
||||
stringProvider.getString(R.string.verification_request_notice, it.id)
|
||||
.toSpannable()
|
||||
.colorizeMatchingText(it.id, colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
|
||||
}
|
||||
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(styledText)
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("sep")
|
||||
}
|
||||
|
||||
when (state.started) {
|
||||
is Loading -> {
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(stringProvider.getString(R.string.verification_request_waiting_for, state.matrixItem.getBestName()))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
bottomSheetVerificationActionItem {
|
||||
id("start")
|
||||
title(stringProvider.getString(R.string.start_verification))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_accent))
|
||||
subTitle(stringProvider.getString(R.string.verification_request_start_notice))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.onClickOnVerificationStart() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onClickOnVerificationStart()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.request
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
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.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.crypto.verification.VerificationAction
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationRequestFragment @Inject constructor(
|
||||
val verificationRequestViewModelFactory: VerificationRequestViewModel.Factory,
|
||||
val controller: VerificationRequestController
|
||||
) : VectorBaseFragment(), VerificationRequestController.Listener {
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationRequestViewModel::class)
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
bottomSheetVerificationRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
controller.update(state)
|
||||
}
|
||||
|
||||
override fun onClickOnVerificationStart() = withState(viewModel) { state ->
|
||||
sharedViewModel.handle(VerificationAction.RequestVerificationByDM(state.matrixItem.id, state.roomId))
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.verification
|
||||
package im.vector.riotx.features.crypto.verification.request
|
||||
|
||||
import com.airbnb.mvrx.*
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
|
@ -27,6 +27,7 @@ import im.vector.matrix.android.internal.crypto.verification.PendingVerification
|
|||
import im.vector.riotx.core.di.HasScreenInjector
|
||||
import im.vector.riotx.core.platform.EmptyAction
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
|
||||
|
||||
data class VerificationRequestViewState(
|
||||
val roomId: String? = null,
|
|
@ -67,6 +67,6 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||
|
||||
data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction()
|
||||
data class DeclineVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction()
|
||||
|
||||
data class RequestVerification(val userId: String) : RoomDetailAction()
|
||||
data class ResumeVerification(val transactionId: String, val otherUserId: String? = null, val otherdDeviceId: String? = null) : RoomDetailAction()
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_ROOM_DETAIL_ARGS = "EXTRA_ROOM_DETAIL_ARGS"
|
||||
const val EXTRA_ROOM_DETAIL_ARGS = "EXTRA_ROOM_DETAIL_ARGS"
|
||||
|
||||
fun newIntent(context: Context, roomDetailArgs: RoomDetailArgs): Intent {
|
||||
return Intent(context, RoomDetailActivity::class.java).apply {
|
||||
|
|
|
@ -54,6 +54,7 @@ import com.airbnb.epoxy.OnModelBuildFinishedListener
|
|||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
|
@ -74,6 +75,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageFileConten
|
|||
import im.vector.matrix.android.api.session.room.model.message.MessageImageInfoContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||
|
@ -849,6 +851,15 @@ class RoomDetailFragment @Inject constructor(
|
|||
data.transactionId
|
||||
).show(parentFragmentManager, "REQ")
|
||||
}
|
||||
is RoomDetailAction.ResumeVerification -> {
|
||||
val otherUserId = data.otherUserId ?: return
|
||||
VerificationBottomSheet().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(MvRx.KEY_ARG, VerificationBottomSheet.VerificationArgs(
|
||||
otherUserId, data.transactionId, roomId = roomDetailArgs.roomId))
|
||||
}
|
||||
}.show(parentFragmentManager, "REQ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -998,6 +1009,9 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View) {
|
||||
if (messageContent is MessageVerificationRequestContent) {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View): Boolean {
|
||||
|
|
|
@ -61,6 +61,7 @@ import im.vector.riotx.core.utils.PublishDataSource
|
|||
import im.vector.riotx.core.utils.subscribeLogError
|
||||
import im.vector.riotx.features.command.CommandParser
|
||||
import im.vector.riotx.features.command.ParsedCommand
|
||||
import im.vector.riotx.features.crypto.verification.supportedVerificationMethods
|
||||
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
||||
import im.vector.riotx.features.home.room.typing.TypingHelper
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
|
@ -187,6 +188,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
|
||||
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
||||
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
||||
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -408,7 +410,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
popDraft()
|
||||
}
|
||||
is ParsedCommand.VerifyUser -> {
|
||||
session.getSasVerificationService().requestKeyVerificationInDMs(slashCommandResult.userId, room.roomId)
|
||||
session.getSasVerificationService().requestKeyVerificationInDMs(supportedVerificationMethods, slashCommandResult.userId, room.roomId)
|
||||
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
|
@ -824,6 +826,18 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
_requestLiveData.postValue(LiveEvent(Success(action)))
|
||||
}
|
||||
|
||||
private fun handleResumeRequestVerification(action: RoomDetailAction.ResumeVerification) {
|
||||
// Check if this request is still active and handled by me
|
||||
session.getSasVerificationService().getExistingVerificationRequestInRoom(room.roomId, action.transactionId)?.let {
|
||||
if (it.handledByOtherSession) return
|
||||
if (!it.isFinished) {
|
||||
_requestLiveData.postValue(LiveEvent(Success(action.copy(
|
||||
otherUserId = it.otherUserId
|
||||
))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeSyncState() {
|
||||
session.rx()
|
||||
.liveSyncState()
|
||||
|
|
|
@ -18,12 +18,8 @@ package im.vector.riotx.features.home.room.detail.readreceipts
|
|||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.args
|
||||
import im.vector.riotx.R
|
||||
|
@ -57,11 +53,7 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list_with_title, container, false)
|
||||
ButterKnife.bind(this, view)
|
||||
return view
|
||||
}
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
|
|
@ -16,12 +16,8 @@
|
|||
package im.vector.riotx.features.home.room.detail.timeline.action
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
|
@ -53,18 +49,12 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
|
|||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list, container, false)
|
||||
ButterKnife.bind(this, view)
|
||||
return view
|
||||
}
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
||||
recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false)
|
||||
// Disable item animation
|
||||
recyclerView.itemAnimator = null
|
||||
recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false, disableItemAnimation = true)
|
||||
messageActionsEpoxyController.listener = this
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,8 @@
|
|||
package im.vector.riotx.features.home.room.detail.timeline.edithistory
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
|
@ -57,11 +53,7 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list_with_title, container, false)
|
||||
ButterKnife.bind(this, view)
|
||||
return view
|
||||
}
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
|
|
@ -17,12 +17,8 @@
|
|||
package im.vector.riotx.features.home.room.detail.timeline.reactions
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
|
@ -54,11 +50,7 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list_with_title, container, false)
|
||||
ButterKnife.bind(this, view)
|
||||
return view
|
||||
}
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
|
|
@ -18,12 +18,8 @@ package im.vector.riotx.features.home.room.list.actions
|
|||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
|
@ -69,18 +65,12 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R
|
|||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list, container, false)
|
||||
ButterKnife.bind(this, view)
|
||||
return view
|
||||
}
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
||||
recyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false)
|
||||
// Disable item animation
|
||||
recyclerView.itemAnimator = null
|
||||
recyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true)
|
||||
roomListActionsEpoxyController.listener = this
|
||||
}
|
||||
|
||||
|
|
|
@ -20,12 +20,12 @@ import android.os.Build
|
|||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tapadoo.alerter.Alerter
|
||||
import com.tapadoo.alerter.OnHideAlertListener
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.features.crypto.verification.SASVerificationActivity
|
||||
import timber.log.Timber
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
@ -78,8 +78,7 @@ object PopupAlertManager {
|
|||
setLightStatusBar()
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldIgnoreActivity(activity)) {
|
||||
if (currentAlerter?.shouldBeDisplayedIn?.invoke(activity) == false) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -108,8 +107,6 @@ object PopupAlertManager {
|
|||
}
|
||||
}
|
||||
|
||||
private fun shouldIgnoreActivity(activity: Activity) = activity is SASVerificationActivity
|
||||
|
||||
private fun displayNextIfPossible() {
|
||||
val currentActivity = weakCurrentActivity?.get()
|
||||
if (Alerter.isShowing || currentActivity == null) {
|
||||
|
@ -209,7 +206,13 @@ object PopupAlertManager {
|
|||
})
|
||||
.enableSwipeToDismiss()
|
||||
.enableInfiniteDuration(true)
|
||||
.setBackgroundColorRes(alert.colorRes ?: R.color.notification_accent_color)
|
||||
.apply {
|
||||
if (alert.colorInt != null) {
|
||||
setBackgroundColorInt(alert.colorInt!!)
|
||||
} else {
|
||||
setBackgroundColorRes(alert.colorRes ?: R.color.notification_accent_color)
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
|
@ -229,7 +232,8 @@ object PopupAlertManager {
|
|||
data class VectorAlert(val uid: String,
|
||||
val title: String,
|
||||
val description: String,
|
||||
@DrawableRes val iconId: Int?) {
|
||||
@DrawableRes val iconId: Int?,
|
||||
val shouldBeDisplayedIn: ((Activity) -> Boolean)? = null) {
|
||||
|
||||
data class Button(val title: String, val action: Runnable, val autoClose: Boolean)
|
||||
|
||||
|
@ -250,5 +254,8 @@ object PopupAlertManager {
|
|||
|
||||
@ColorRes
|
||||
var colorRes: Int? = null
|
||||
|
||||
@ColorInt
|
||||
var colorInt: Int? = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import android.app.Activity
|
|||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
|
@ -32,7 +31,6 @@ import androidx.core.view.isVisible
|
|||
import androidx.lifecycle.Observer
|
||||
import androidx.transition.TransitionManager
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
||||
|
@ -210,11 +208,7 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment() {
|
|||
})
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.bottom_sheet_logout_and_backup, container, false)
|
||||
ButterKnife.bind(this, view)
|
||||
return view
|
||||
}
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_logout_and_backup
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
|
|
22
vector/src/main/res/drawable/ic_camera.xml
Normal file
22
vector/src/main/res/drawable/ic_camera.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M23,19C23,20.1046 22.1046,21 21,21H3C1.8954,21 1,20.1046 1,19V8C1,6.8954 1.8954,6 3,6H7L9,3H15L17,6H21C22.1046,6 23,6.8954 23,8V19Z"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#03B381"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M12,17C14.2091,17 16,15.2091 16,13C16,10.7909 14.2091,9 12,9C9.7909,9 8,10.7909 8,13C8,15.2091 9.7909,17 12,17Z"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#03B381"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
20
vector/src/main/res/drawable/ic_check_off.xml
Normal file
20
vector/src/main/res/drawable/ic_check_off.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M18,6L6,18"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#ff4b55"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M6,6L18,18"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#ff4b55"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
13
vector/src/main/res/drawable/ic_check_on.xml
Normal file
13
vector/src/main/res/drawable/ic_check_on.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M20,7L9,18L4,13"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#03B381"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.core.widget.NestedScrollView 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:id="@+id/bottomSheetScrollView"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -9,47 +10,62 @@
|
|||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
<ImageView
|
||||
android:id="@+id/verificationRequestAvatar"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="@drawable/circle"
|
||||
android:contentDescription="@string/avatar"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verificationRequestShield"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_shield_trusted"
|
||||
app:layout_constraintCircle="@+id/verificationRequestAvatar"
|
||||
app:layout_constraintCircleAngle="135"
|
||||
app:layout_constraintCircleRadius="16dp"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verificationRequestName"
|
||||
android:layout_width="0dp"
|
||||
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>
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/verificationRequestAvatar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/verificationRequestAvatar"
|
||||
app:layout_constraintTop_toTopOf="@+id/verificationRequestAvatar"
|
||||
tools:text="@string/verification_verify_user" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/bottomSheetFragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp" />
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/verificationRequestAvatar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/bottomSheetVerificationRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical"
|
||||
tools:itemCount="5"
|
||||
tools:listitem="@layout/item_verification_action" />
|
|
@ -1,188 +0,0 @@
|
|||
<?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:background="?android:colorBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_emoji_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:text="@string/verify_by_emoji_title"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_emoji_description_2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:text="@string/verify_user_sas_emoji_help_text"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_emoji_description" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_decimal_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/sas_emoji_grid"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/sas_emoji_grid"
|
||||
tools:text="1234-4320-3905"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/sasLoadingProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/sas_emoji_grid"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/sas_emoji_grid" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/sas_emoji_grid"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:visibility="invisible"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_description_2"
|
||||
tools:visibility="visible">
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji0"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji1"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji2"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji3"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji4"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji5"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji6"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<androidx.constraintlayout.helper.widget.Flow
|
||||
android:id="@+id/sas_emoji_grid_flow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="emoji0,emoji1,emoji2,emoji3,emoji4,emoji5,emoji6"
|
||||
app:flow_horizontalBias="0.5"
|
||||
app:flow_horizontalGap="16dp"
|
||||
app:flow_horizontalStyle="packed"
|
||||
app:flow_verticalBias="0"
|
||||
app:flow_verticalGap="8dp"
|
||||
app:flow_wrapMode="chain"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_request_continue_button"
|
||||
style="@style/VectorButtonStylePositive"
|
||||
android:layout_width="0dp"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:minWidth="160dp"
|
||||
android:text="@string/verification_sas_match"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/centerGuideLine"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_grid"
|
||||
tools:text="A very long translation thats too big" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/centerGuideLine"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_request_cancel_button"
|
||||
style="@style/VectorButtonStyleDestructive"
|
||||
android:layout_width="0dp"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:text="@string/verification_sas_do_not_match"
|
||||
app:layout_constraintEnd_toStartOf="@+id/centerGuideLine"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_grid" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/bottomBarrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:barrierMargin="8dp"
|
||||
app:constraint_referenced_ids="sas_request_cancel_button,sas_request_continue_button" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sasCodeWaitingPartnerText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/sas_waiting_for_partner"
|
||||
android:textColor="?attr/vctr_notice_secondary"
|
||||
android:textSize="15sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomBarrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_emoji_grid"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sasEmojiSecurityTip"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:gravity="center"
|
||||
android:text="@string/verify_user_sas_emoji_security_tip"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bottomBarrier" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/ButtonsVisibilityGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="sas_request_continue_button,sas_request_cancel_button"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,132 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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="match_parent"
|
||||
android:background="?android:colorBackground">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_emoji_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:gravity="center"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/sas_emoji_description" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_emoji_description_2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:gravity="center"
|
||||
android:text="@string/sas_security_advise"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_emoji_description" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_decimal_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/sas_emoji_grid"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/sas_emoji_grid"
|
||||
tools:text="1234-4320-3905"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/sas_emoji_grid"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:visibility="invisible"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_description_2"
|
||||
tools:visibility="visible">
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji0"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji1"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji2"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji3"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji4"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji5"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji6"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<androidx.constraintlayout.helper.widget.Flow
|
||||
android:id="@+id/sas_emoji_grid_flow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="emoji0,emoji1,emoji2,emoji3,emoji4,emoji5,emoji6"
|
||||
app:flow_horizontalBias="0.5"
|
||||
app:flow_horizontalGap="16dp"
|
||||
app:flow_horizontalStyle="packed"
|
||||
app:flow_verticalBias="0"
|
||||
app:flow_verticalGap="8dp"
|
||||
app:flow_wrapMode="chain"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_request_continue_button"
|
||||
style="@style/VectorButtonStyle"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:minWidth="160dp"
|
||||
android:text="@string/_continue"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_grid" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_request_cancel_button"
|
||||
style="@style/VectorButtonStyleText"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:text="@string/cancel"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sas_request_continue_button"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_grid" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
|
@ -1,132 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_incoming_request_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/sas_incoming_request_title"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sas_incoming_request_user_avatar"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_incoming_request_title"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_incoming_request_user_display_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_user_avatar"
|
||||
tools:text="User name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_incoming_request_user_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_user_display_name"
|
||||
tools:text="\@foo:matrix.org" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_incoming_request_user_device"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_user_id"
|
||||
tools:text="Device: Mobile" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_incoming_request_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:text="@string/sas_incoming_request_description"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_user_device" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_incoming_request_description_2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:text="@string/sas_incoming_request_description_2"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_description" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_request_continue_button"
|
||||
style="@style/VectorButtonStyle"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin_big"
|
||||
android:layout_marginEnd="@dimen/layout_vertical_margin"
|
||||
android:layout_marginRight="@dimen/layout_vertical_margin"
|
||||
android:layout_marginBottom="@dimen/layout_vertical_margin"
|
||||
android:minWidth="160dp"
|
||||
android:text="@string/_continue"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_incoming_request_description_2" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_request_cancel_button"
|
||||
style="@style/VectorButtonStyleText"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginEnd="@dimen/layout_vertical_margin"
|
||||
android:layout_marginRight="@dimen/layout_vertical_margin"
|
||||
android:text="@string/cancel"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sas_request_continue_button"
|
||||
app:layout_constraintTop_toTopOf="@+id/sas_request_continue_button" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
|
@ -1,89 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rootLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_verification_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/sas_verify_title"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_verification_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/sas_security_advise"
|
||||
android:textColor="?riotx_text_secondary" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_cancel_button"
|
||||
style="@style/VectorButtonStyleText"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:text="@string/cancel" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/layout_horizontal_margin">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_start_button"
|
||||
style="@style/VectorButtonStyle"
|
||||
android:minWidth="160dp"
|
||||
android:text="@string/sas_verify_start_button_title" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/sas_start_button_loading"
|
||||
android:layout_width="19dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_legacy_verification"
|
||||
style="@style/VectorButtonStyleText"
|
||||
android:layout_gravity="end"
|
||||
android:layout_margin="@dimen/layout_horizontal_margin"
|
||||
android:text="@string/sas_legacy_verification_button_title"
|
||||
android:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_verifying_keys"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/sas_verifying_keys"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
|
@ -1,69 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:colorBackground">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_verification_verified_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/sas_verified"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_verification_verified_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:gravity="center"
|
||||
android:text="@string/sas_verified_successful"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_verification_verified_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sas_verification_verified_description_2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_vertical_margin"
|
||||
android:gravity="center"
|
||||
android:text="@string/sas_verified_successful_description"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sas_verification_verified_description" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sas_verification_verified_done_button"
|
||||
style="@style/VectorButtonStyle"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin_big"
|
||||
android:layout_marginEnd="@dimen/layout_vertical_margin"
|
||||
android:layout_marginRight="@dimen/layout_vertical_margin"
|
||||
android:layout_marginBottom="@dimen/layout_vertical_margin"
|
||||
android:minWidth="160dp"
|
||||
android:text="@string/sas_got_it"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sas_verification_verified_description_2" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
|
@ -1,117 +0,0 @@
|
|||
<?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/verificationQRTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/verify_by_scanning_title"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verifyQRDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/verify_by_scanning_description"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/verificationQRTitle"
|
||||
tools:text="@string/verify_by_scanning_description" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verifyQRImageView"
|
||||
android:layout_width="180dp"
|
||||
android:layout_height="180dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="?riotx_header_panel_background"
|
||||
android:contentDescription="@string/aria_qr_code_description"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/verifyQRDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verificationEmojiTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:text="@string/verify_by_emoji_title"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintTop_toBottomOf="@id/verifyQRImageView"
|
||||
app:layout_goneMarginTop="0dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verifyEmojiDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/verify_by_emoji_description"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/verificationEmojiTitle" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/verificationByEmojiButton"
|
||||
style="@style/VectorButtonStylePositive"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/verify_by_emoji_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/verifyEmojiDescription" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/verifyQRGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:constraint_referenced_ids="verifyQRDescription,verificationQRTitle,verifyQRImageView" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/verifyEmojiGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:constraint_referenced_ids="verifyEmojiDescription,verificationEmojiTitle,verificationByEmojiButton" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,63 +0,0 @@
|
|||
<?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">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verificationConclusionTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/sas_verified"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/sas_verified" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verifyConclusionDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/sas_verified_successful_description"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/verificationConclusionTitle"
|
||||
tools:text="@string/sas_verified_successful_description" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verifyConclusionImageView"
|
||||
android:layout_width="180dp"
|
||||
android:layout_height="180dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/verifyConclusionDescription"
|
||||
tools:background="@drawable/ic_shield_trusted" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verifyConclusionBottomDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/verifyConclusionImageView"
|
||||
tools:text="@string/verification_green_shield" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/verificationConclusionButton"
|
||||
style="@style/VectorButtonStylePositive"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/sas_got_it"
|
||||
app:layout_constraintTop_toBottomOf="@id/verifyConclusionBottomDescription" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,43 +0,0 @@
|
|||
<?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:padding="16dp">
|
||||
|
||||
<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" />
|
||||
|
||||
<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"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="@id/verificationStartButton"
|
||||
app:layout_constraintTop_toTopOf="@id/verificationStartButton"
|
||||
tools:text="@string/verification_request_waiting_for" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
57
vector/src/main/res/layout/item_verification_action.xml
Normal file
57
vector/src/main/res/layout/item_verification_action.xml
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?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:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:minHeight="72dp"
|
||||
android:paddingLeft="@dimen/layout_horizontal_margin"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="@dimen/layout_horizontal_margin"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemVerificationActionTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/riotx_accent"
|
||||
android:textSize="16sp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemVerificationActionSubTitle"
|
||||
app:layout_constraintEnd_toStartOf="@+id/itemVerificationActionIcon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="@string/start_verification" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemVerificationActionSubTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/itemVerificationActionIcon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/itemVerificationActionTitle"
|
||||
tools:text="For maximum security, do this in person"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemVerificationActionIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="center"
|
||||
android:tint="?riotx_text_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_arrow_right" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/itemVerificationBigImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="180dp"
|
||||
android:layout_margin="8dp"
|
||||
android:src="@drawable/ic_shield_trusted" />
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView 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:id="@+id/itemVerificationDecimalCode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/sas_emoji_grid"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/sas_emoji_grid"
|
||||
tools:text="1234-4320-3905" />
|
50
vector/src/main/res/layout/item_verification_emojis.xml
Normal file
50
vector/src/main/res/layout/item_verification_emojis.xml
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?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:layout_margin="@dimen/layout_vertical_margin">
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji0"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji1"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji2"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji3"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji4"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji5"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emoji6"
|
||||
layout="@layout/item_emoji_verif" />
|
||||
|
||||
<androidx.constraintlayout.helper.widget.Flow
|
||||
android:id="@+id/sas_emoji_grid_flow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="emoji0,emoji1,emoji2,emoji3,emoji4,emoji5,emoji6"
|
||||
app:flow_horizontalBias="0.5"
|
||||
app:flow_horizontalGap="16dp"
|
||||
app:flow_horizontalStyle="packed"
|
||||
app:flow_verticalBias="0"
|
||||
app:flow_verticalGap="8dp"
|
||||
app:flow_wrapMode="chain"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
13
vector/src/main/res/layout/item_verification_notice.xml
Normal file
13
vector/src/main/res/layout/item_verification_notice.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/itemVerificationNoticeText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@dimen/layout_horizontal_margin"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="@dimen/layout_horizontal_margin"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="14sp"
|
||||
tools:text="todo" />
|
34
vector/src/main/res/layout/item_verification_waiting.xml
Normal file
34
vector/src/main/res/layout/item_verification_waiting.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?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:minHeight="72dp"
|
||||
android:paddingLeft="@dimen/layout_horizontal_margin"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="@dimen/layout_horizontal_margin"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemVerificationWaitingTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/itemVerificationWaitingProgress"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Waiting for..." />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/itemVerificationWaitingProgress"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -35,18 +35,20 @@
|
|||
<!-- 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="verification_scan_notice">Scan the code with the other user\'s device to securely verify each other</string>
|
||||
<string name="verification_scan_their_code">Scan their code</string>
|
||||
<string name="verification_scan_emoji_title">Can\'t scan</string>
|
||||
<string name="verification_scan_emoji_subtitle">If you\'re not in person, compare emoji instead</string>
|
||||
|
||||
<string name="verification_no_scan_emoji_title">Continue</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="a13n_qr_code_description">QR code image</string>
|
||||
|
||||
<string name="verification_request_alert_title">Verify %s</string>
|
||||
<string name="verification_verify_user">Verify %s</string>
|
||||
<string name="verification_verified_user">Verified %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>
|
||||
<string name="room_profile_not_encrypted_subtitle">Messages in this room are not end-to-end encrypted.</string>
|
||||
|
@ -85,4 +87,12 @@
|
|||
<string name="settings_category_composer">Message editor</string>
|
||||
|
||||
|
||||
|
||||
<string name="verification_request_notice">For extra security, verify %s by checking a one-time code.</string>
|
||||
<string name="verification_request_start_notice">For maximum security, do this in person.</string>
|
||||
|
||||
<string name="verification_emoji_notice">Compare the unique emoji, ensuring they appear in the same order.</string>
|
||||
<string name="verification_code_notice">Compare the code with the one displayed on the other user\'s screen.</string>
|
||||
<string name="verification_conclusion_ok_notice">Messages with this user are end-to-end encrypted and can\'t be read by third parties.</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue