mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-16 12:00:03 +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
.idea/codeStyles/codeStyleConfig.xml
generated
1
.idea/codeStyles/codeStyleConfig.xml
generated
|
@ -1,5 +1,6 @@
|
||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<state>
|
<state>
|
||||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
</state>
|
</state>
|
||||||
</component>
|
</component>
|
|
@ -44,22 +44,18 @@ interface SasVerificationService {
|
||||||
|
|
||||||
fun getExistingVerificationRequest(otherUser: String, tid: String?): PendingVerificationRequest?
|
fun getExistingVerificationRequest(otherUser: String, tid: String?): PendingVerificationRequest?
|
||||||
|
|
||||||
/**
|
fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest?
|
||||||
* Shortcut for KeyVerificationStart.VERIF_METHOD_SAS
|
|
||||||
* @see beginKeyVerification
|
fun beginKeyVerification(method: VerificationMethod, userId: String, deviceID: String): String?
|
||||||
*/
|
|
||||||
fun beginKeyVerificationSAS(userId: String, deviceID: String): String?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request a key verification from another user using toDevice events.
|
* Request a key verification from another user using toDevice events.
|
||||||
*/
|
*/
|
||||||
fun beginKeyVerification(method: String, userId: String, deviceID: String): String?
|
fun requestKeyVerificationInDMs(methods: List<VerificationMethod>, userId: String, roomId: String): PendingVerificationRequest
|
||||||
|
|
||||||
fun requestKeyVerificationInDMs(userId: String, roomId: String): PendingVerificationRequest
|
|
||||||
|
|
||||||
fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String)
|
fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String)
|
||||||
|
|
||||||
fun beginKeyVerificationInDMs(method: String,
|
fun beginKeyVerificationInDMs(method: VerificationMethod,
|
||||||
transactionId: String,
|
transactionId: String,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
otherUserId: String,
|
otherUserId: String,
|
||||||
|
@ -67,7 +63,7 @@ interface SasVerificationService {
|
||||||
callback: MatrixCallback<String>?): String?
|
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
|
fun readyPendingVerificationInDMs(otherUserId: String, roomId: String, transactionId: String): Boolean
|
||||||
|
|
||||||
|
@ -84,14 +80,14 @@ interface SasVerificationService {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val TEN_MINTUTES_IN_MILLIS = 10 * 60 * 1000
|
private const val TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000
|
||||||
private const val FIVE_MINTUTES_IN_MILLIS = 5 * 60 * 1000
|
private const val FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000
|
||||||
|
|
||||||
fun isValidRequest(age: Long?): Boolean {
|
fun isValidRequest(age: Long?): Boolean {
|
||||||
if (age == null) return false
|
if (age == null) return false
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
val tooInThePast = now - TEN_MINTUTES_IN_MILLIS
|
val tooInThePast = now - TEN_MINUTES_IN_MILLIS
|
||||||
val tooInTheFuture = now + FIVE_MINTUTES_IN_MILLIS
|
val tooInTheFuture = now + FIVE_MINUTES_IN_MILLIS
|
||||||
return age in tooInThePast..tooInTheFuture
|
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.crypto.sas.SasMode
|
||||||
import im.vector.matrix.android.api.session.events.model.toContent
|
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.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.SASVerificationTransaction
|
||||||
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart
|
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart
|
||||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||||
|
@ -45,12 +45,11 @@ internal data class MessageVerificationStartContent(
|
||||||
get() = relatesTo?.eventId
|
get() = relatesTo?.eventId
|
||||||
|
|
||||||
override fun isValid(): Boolean {
|
override fun isValid(): Boolean {
|
||||||
if (
|
if (transactionID.isNullOrBlank()
|
||||||
(transactionID.isNullOrBlank()
|
|| fromDevice.isNullOrBlank()
|
||||||
|| fromDevice.isNullOrBlank()
|
|| method !in supportedVerificationMethods
|
||||||
|| method != KeyVerificationStart.VERIF_METHOD_SAS
|
|| keyAgreementProtocols.isNullOrEmpty()
|
||||||
|| keyAgreementProtocols.isNullOrEmpty()
|
|| hashes.isNullOrEmpty()
|
||||||
|| hashes.isNullOrEmpty())
|
|
||||||
|| !hashes.contains("sha256") || messageAuthenticationCodes.isNullOrEmpty()
|
|| !hashes.contains("sha256") || messageAuthenticationCodes.isNullOrEmpty()
|
||||||
|| (!messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256)
|
|| (!messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256)
|
||||||
&& !messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|
&& !messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|
||||||
|
|
|
@ -25,8 +25,7 @@ import im.vector.matrix.android.internal.crypto.verification.VerificationInfoRea
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class KeyVerificationReady(
|
internal data class KeyVerificationReady(
|
||||||
@Json(name = "from_device") override val fromDevice: String?,
|
@Json(name = "from_device") override val fromDevice: String?,
|
||||||
// TODO add qr?
|
@Json(name = "methods") override val methods: List<String>?,
|
||||||
@Json(name = "methods") override val methods: List<String>? = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
|
|
||||||
@Json(name = "transaction_id") override var transactionID: String? = null
|
@Json(name = "transaction_id") override var transactionID: String? = null
|
||||||
) : SendToDeviceObject, VerificationInfoReady {
|
) : SendToDeviceObject, VerificationInfoReady {
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ internal data class KeyVerificationRequest(
|
||||||
@Json(name = "from_device")
|
@Json(name = "from_device")
|
||||||
val fromDevice: String,
|
val fromDevice: String,
|
||||||
/** The verification methods supported by the sender. */
|
/** 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.
|
* 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,
|
* 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.
|
* Sent by Alice to initiate an interactive key verification.
|
||||||
*/
|
*/
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class KeyVerificationStart(
|
internal data class KeyVerificationStart(
|
||||||
@Json(name = "from_device") override val fromDevice: String? = null,
|
@Json(name = "from_device") override val fromDevice: String? = null,
|
||||||
override val method: String? = null,
|
override val method: String? = null,
|
||||||
@Json(name = "transaction_id") override val transactionID: 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)
|
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 {
|
override fun isValid(): Boolean {
|
||||||
if ((transactionID.isNullOrBlank()
|
if (transactionID.isNullOrBlank()
|
||||||
|| fromDevice.isNullOrBlank()
|
|| fromDevice.isNullOrBlank()
|
||||||
|| method != VERIF_METHOD_SAS
|
|| method !in supportedVerificationMethods
|
||||||
|| keyAgreementProtocols.isNullOrEmpty()
|
|| keyAgreementProtocols.isNullOrEmpty()
|
||||||
|| hashes.isNullOrEmpty())
|
|| hashes.isNullOrEmpty()
|
||||||
|| !hashes.contains("sha256")
|
|| !hashes.contains("sha256")
|
||||||
|| messageAuthenticationCodes.isNullOrEmpty()
|
|| messageAuthenticationCodes.isNullOrEmpty()
|
||||||
|| (!messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256)
|
|| (!messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256)
|
||||||
&& !messageAuthenticationCodes.contains(SASVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|
&& !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")
|
Timber.e("## received invalid verification request")
|
||||||
return false
|
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
|
// The verification is started from another device
|
||||||
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
|
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
|
||||||
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
||||||
|
params.sasVerificationService.onRoomRequestHandledByOtherDevice(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (EventType.KEY_VERIFICATION_READY == event.type) {
|
} else if (EventType.KEY_VERIFICATION_READY == event.type) {
|
||||||
|
@ -112,11 +113,13 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
|
||||||
// The verification is started from another device
|
// The verification is started from another device
|
||||||
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
|
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
|
||||||
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
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) {
|
} else if (EventType.KEY_VERIFICATION_CANCEL == event.type || EventType.KEY_VERIFICATION_DONE == event.type) {
|
||||||
event.getClearContent().toModel<MessageRelationContent>()?.relatesTo?.eventId?.let {
|
event.getClearContent().toModel<MessageRelationContent>()?.relatesTo?.eventId?.let {
|
||||||
transactionsHandledByOtherDevice.remove(it)
|
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.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
|
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.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.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
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 im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ internal class DefaultOutgoingSASVerificationRequest(
|
||||||
cancel(CancelCode.UnexpectedMessage)
|
cancel(CancelCode.UnexpectedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun start() {
|
fun start(method: VerificationMethod) {
|
||||||
if (state != SasVerificationTxState.None) {
|
if (state != SasVerificationTxState.None) {
|
||||||
Timber.e("## SAS O: start verification from invalid state")
|
Timber.e("## SAS O: start verification from invalid state")
|
||||||
// should I cancel??
|
// should I cancel??
|
||||||
|
@ -80,7 +81,7 @@ internal class DefaultOutgoingSASVerificationRequest(
|
||||||
|
|
||||||
val startMessage = transport.createStart(
|
val startMessage = transport.createStart(
|
||||||
credentials.deviceId ?: "",
|
credentials.deviceId ?: "",
|
||||||
KeyVerificationStart.VERIF_METHOD_SAS,
|
method.toValue(),
|
||||||
transactionId,
|
transactionId,
|
||||||
KNOWN_AGREEMENT_PROTOCOLS,
|
KNOWN_AGREEMENT_PROTOCOLS,
|
||||||
KNOWN_HASHES,
|
KNOWN_HASHES,
|
||||||
|
|
|
@ -22,10 +22,7 @@ import dagger.Lazy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
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.CryptoService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.sas.*
|
||||||
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.events.model.Event
|
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.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
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}")
|
Timber.v("## SAS Verification request from ${event.senderId} in room ${event.roomId}")
|
||||||
val requestInfo = event.getClearContent().toModel<MessageVerificationRequestContent>()
|
val requestInfo = event.getClearContent().toModel<MessageVerificationRequestContent>()
|
||||||
?: return
|
?: return
|
||||||
|
@ -245,6 +255,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
ageLocalTs = event.ageLocalTs ?: System.currentTimeMillis(),
|
ageLocalTs = event.ageLocalTs ?: System.currentTimeMillis(),
|
||||||
isIncoming = true,
|
isIncoming = true,
|
||||||
otherUserId = senderId, // requestInfo.toUserId,
|
otherUserId = senderId, // requestInfo.toUserId,
|
||||||
|
roomId = event.roomId,
|
||||||
transactionId = event.eventId,
|
transactionId = event.eventId,
|
||||||
requestInfo = requestInfo
|
requestInfo = requestInfo
|
||||||
)
|
)
|
||||||
|
@ -274,31 +285,27 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
if (startReq?.isValid()?.not() == true) {
|
if (startReq?.isValid()?.not() == true) {
|
||||||
Timber.e("## received invalid verification request")
|
Timber.e("## received invalid verification request")
|
||||||
if (startReq.transactionID != null) {
|
if (startReq.transactionID != null) {
|
||||||
sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
sasTransportRoomMessageFactory.createTransport(event.roomId ?: "", null)
|
||||||
?: "", event.roomId
|
.cancelTransaction(
|
||||||
?: "", null).cancelTransaction(
|
startReq.transactionID ?: "",
|
||||||
startReq.transactionID ?: "",
|
otherUserId!!,
|
||||||
otherUserId!!,
|
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||||
startReq.fromDevice ?: event.getSenderKey()!!,
|
CancelCode.UnknownMethod
|
||||||
CancelCode.UnknownMethod
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStart(otherUserId, startReq as VerificationInfoStart) {
|
handleStart(otherUserId, startReq as VerificationInfoStart) {
|
||||||
it.transport = sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
it.transport = sasTransportRoomMessageFactory.createTransport(event.roomId ?: "", it)
|
||||||
?: "", event.roomId
|
|
||||||
?: "", it)
|
|
||||||
}?.let {
|
}?.let {
|
||||||
sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
sasTransportRoomMessageFactory.createTransport(event.roomId ?: "", null)
|
||||||
?: "", event.roomId
|
.cancelTransaction(
|
||||||
?: "", null).cancelTransaction(
|
startReq.transactionID ?: "",
|
||||||
startReq.transactionID ?: "",
|
otherUserId!!,
|
||||||
otherUserId!!,
|
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||||
startReq.fromDevice ?: event.getSenderKey()!!,
|
it
|
||||||
it
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +363,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
// cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
// cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||||
} else {
|
} else {
|
||||||
// Ok we can create
|
// 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!!}")
|
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
|
||||||
// If there is a corresponding request, we can auto accept
|
// If there is a corresponding request, we can auto accept
|
||||||
// as we are the one requesting in first place (or we accepted the request)
|
// 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>? {
|
private fun getExistingTransactionsForUser(otherUser: String): Collection<VerificationTransaction>? {
|
||||||
synchronized(txMap) {
|
synchronized(txMap) {
|
||||||
return txMap[otherUser]?.values
|
return txMap[otherUser]?.values
|
||||||
|
@ -670,14 +687,10 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beginKeyVerificationSAS(userId: String, deviceID: String): String? {
|
override fun beginKeyVerification(method: VerificationMethod, userId: String, deviceID: String): String? {
|
||||||
return beginKeyVerification(KeyVerificationStart.VERIF_METHOD_SAS, userId, deviceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun beginKeyVerification(method: String, userId: String, deviceID: String): String? {
|
|
||||||
val txID = createUniqueIDForTransaction(userId, deviceID)
|
val txID = createUniqueIDForTransaction(userId, deviceID)
|
||||||
// should check if already one (and cancel it)
|
// should check if already one (and cancel it)
|
||||||
if (KeyVerificationStart.VERIF_METHOD_SAS == method) {
|
if (method == VerificationMethod.SAS) {
|
||||||
val tx = DefaultOutgoingSASVerificationRequest(
|
val tx = DefaultOutgoingSASVerificationRequest(
|
||||||
setDeviceVerificationAction,
|
setDeviceVerificationAction,
|
||||||
credentials,
|
credentials,
|
||||||
|
@ -689,14 +702,14 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
tx.transport = sasTransportToDeviceFactory.createTransport(tx)
|
tx.transport = sasTransportToDeviceFactory.createTransport(tx)
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
|
|
||||||
tx.start()
|
tx.start(method)
|
||||||
return txID
|
return txID
|
||||||
} else {
|
} else {
|
||||||
throw IllegalArgumentException("Unknown verification method")
|
throw IllegalArgumentException("Unknown verification method")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requestKeyVerificationInDMs(userId: String, roomId: String)
|
override fun requestKeyVerificationInDMs(methods: List<VerificationMethod>, userId: String, roomId: String)
|
||||||
: PendingVerificationRequest {
|
: PendingVerificationRequest {
|
||||||
Timber.i("## SAS Requesting verification to user: $userId in room $roomId")
|
Timber.i("## SAS Requesting verification to user: $userId in room $roomId")
|
||||||
|
|
||||||
|
@ -705,8 +718,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
pendingRequests[userId] = it
|
pendingRequests[userId] = it
|
||||||
}
|
}
|
||||||
|
|
||||||
val transport = sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
val transport = sasTransportRoomMessageFactory.createTransport(roomId, null)
|
||||||
?: "", roomId, null)
|
|
||||||
|
|
||||||
// Cancel existing pending requests?
|
// Cancel existing pending requests?
|
||||||
requestsForUser.forEach { existingRequest ->
|
requestsForUser.forEach { existingRequest ->
|
||||||
|
@ -723,11 +735,12 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
val verificationRequest = PendingVerificationRequest(
|
val verificationRequest = PendingVerificationRequest(
|
||||||
ageLocalTs = System.currentTimeMillis(),
|
ageLocalTs = System.currentTimeMillis(),
|
||||||
isIncoming = false,
|
isIncoming = false,
|
||||||
|
roomId = roomId,
|
||||||
localID = localID,
|
localID = localID,
|
||||||
otherUserId = userId
|
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
|
// We need to update with the syncedID
|
||||||
updatePendingRequest(verificationRequest.copy(
|
updatePendingRequest(verificationRequest.copy(
|
||||||
transactionId = syncedId,
|
transactionId = syncedId,
|
||||||
|
@ -742,8 +755,8 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String) {
|
override fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String) {
|
||||||
sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
sasTransportRoomMessageFactory.createTransport(roomId, null)
|
||||||
?: "", roomId, null).cancelTransaction(transactionId, otherUserId, otherDeviceId, CancelCode.User)
|
.cancelTransaction(transactionId, otherUserId, otherDeviceId, CancelCode.User)
|
||||||
|
|
||||||
getExistingVerificationRequest(otherUserId, transactionId)?.let {
|
getExistingVerificationRequest(otherUserId, transactionId)?.let {
|
||||||
updatePendingRequest(it.copy(
|
updatePendingRequest(it.copy(
|
||||||
|
@ -768,10 +781,13 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
dispatchRequestUpdated(updated)
|
dispatchRequestUpdated(updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beginKeyVerificationInDMs(method: String, transactionId: String, roomId: String,
|
override fun beginKeyVerificationInDMs(method: VerificationMethod,
|
||||||
otherUserId: String, otherDeviceId: String,
|
transactionId: String,
|
||||||
|
roomId: String,
|
||||||
|
otherUserId: String,
|
||||||
|
otherDeviceId: String,
|
||||||
callback: MatrixCallback<String>?): String? {
|
callback: MatrixCallback<String>?): String? {
|
||||||
if (KeyVerificationStart.VERIF_METHOD_SAS == method) {
|
if (method == VerificationMethod.SAS) {
|
||||||
val tx = DefaultOutgoingSASVerificationRequest(
|
val tx = DefaultOutgoingSASVerificationRequest(
|
||||||
setDeviceVerificationAction,
|
setDeviceVerificationAction,
|
||||||
credentials,
|
credentials,
|
||||||
|
@ -780,11 +796,10 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
transactionId,
|
transactionId,
|
||||||
otherUserId,
|
otherUserId,
|
||||||
otherDeviceId)
|
otherDeviceId)
|
||||||
tx.transport = sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
tx.transport = sasTransportRoomMessageFactory.createTransport(roomId, tx)
|
||||||
?: "", roomId, tx)
|
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
|
|
||||||
tx.start()
|
tx.start(method)
|
||||||
return transactionId
|
return transactionId
|
||||||
} else {
|
} else {
|
||||||
throw IllegalArgumentException("Unknown verification method")
|
throw IllegalArgumentException("Unknown verification method")
|
||||||
|
@ -797,9 +812,8 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||||
val existingRequest = getExistingVerificationRequest(otherUserId, transactionId)
|
val existingRequest = getExistingVerificationRequest(otherUserId, transactionId)
|
||||||
if (existingRequest != null) {
|
if (existingRequest != null) {
|
||||||
// we need to send a ready event, with matching methods
|
// we need to send a ready event, with matching methods
|
||||||
val transport = sasTransportRoomMessageFactory.createTransport(credentials.userId, credentials.deviceId
|
val transport = sasTransportRoomMessageFactory.createTransport(roomId, null)
|
||||||
?: "", roomId, null)
|
val methods = existingRequest.requestInfo?.methods?.intersect(supportedVerificationMethods)?.toList()
|
||||||
val methods = existingRequest.requestInfo?.methods?.intersect(listOf(KeyVerificationStart.VERIF_METHOD_SAS))?.toList()
|
|
||||||
if (methods.isNullOrEmpty()) {
|
if (methods.isNullOrEmpty()) {
|
||||||
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
|
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
|
||||||
// TODO buttons should not be shown in this case?
|
// TODO buttons should not be shown in this case?
|
||||||
|
|
|
@ -16,27 +16,38 @@
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
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.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.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.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores current pending verification requests
|
* Stores current pending verification requests
|
||||||
*/
|
*/
|
||||||
data class PendingVerificationRequest(
|
data class PendingVerificationRequest(
|
||||||
val ageLocalTs : Long,
|
val ageLocalTs: Long,
|
||||||
val isIncoming: Boolean = false,
|
val isIncoming: Boolean = false,
|
||||||
val localID: String = UUID.randomUUID().toString(),
|
val localID: String = UUID.randomUUID().toString(),
|
||||||
val otherUserId: String,
|
val otherUserId: String,
|
||||||
|
val roomId: String?,
|
||||||
val transactionId: String? = null,
|
val transactionId: String? = null,
|
||||||
val requestInfo: MessageVerificationRequestContent? = null,
|
val requestInfo: MessageVerificationRequestContent? = null,
|
||||||
val readyInfo: VerificationInfoReady? = null,
|
val readyInfo: VerificationInfoReady? = null,
|
||||||
val cancelConclusion: CancelCode? = null,
|
val cancelConclusion: CancelCode? = null,
|
||||||
val isSuccessful : Boolean = false
|
val isSuccessful: Boolean = false,
|
||||||
|
val handledByOtherSession: Boolean = false
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val isReady: Boolean = readyInfo != null
|
val isReady: Boolean = readyInfo != null
|
||||||
val isSent: Boolean = transactionId != null
|
val isSent: Boolean = transactionId != null
|
||||||
|
|
||||||
val isFinished: Boolean = isSuccessful || cancelConclusion != 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,
|
onErrorReason: CancelCode,
|
||||||
onDone: (() -> Unit)?)
|
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)
|
fun done(transactionId: String)
|
||||||
/**
|
/**
|
||||||
|
@ -58,7 +65,7 @@ internal interface SasTransport {
|
||||||
keyAgreementProtocols: List<String>,
|
keyAgreementProtocols: List<String>,
|
||||||
hashes: List<String>,
|
hashes: List<String>,
|
||||||
messageAuthenticationCodes: List<String>,
|
messageAuthenticationCodes: List<String>,
|
||||||
shortAuthenticationStrings: List<String>) : VerificationInfoStart
|
shortAuthenticationStrings: List<String>): VerificationInfoStart
|
||||||
|
|
||||||
fun createMac(tid: String, mac: Map<String, String>, keys: String): VerificationInfoMac
|
fun createMac(tid: String, mac: Map<String, String>, keys: String): VerificationInfoMac
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import com.zhuinden.monarchy.Monarchy
|
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.events.model.*
|
||||||
import im.vector.matrix.android.api.session.room.model.message.*
|
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.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.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 im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
@ -38,9 +40,11 @@ import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SasTransportRoomMessage(
|
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 userId: String,
|
||||||
private val userDevice: String,
|
private val userDeviceId: String?,
|
||||||
private val roomId: String,
|
private val roomId: String,
|
||||||
private val monarchy: Monarchy,
|
private val monarchy: Monarchy,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
|
@ -61,7 +65,7 @@ internal class SasTransportRoomMessage(
|
||||||
)
|
)
|
||||||
|
|
||||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||||
userId = userId,
|
sessionId = sessionId,
|
||||||
event = event
|
event = event
|
||||||
))
|
))
|
||||||
val enqueueInfo = enqueueSendWork(workerParams)
|
val enqueueInfo = enqueueSendWork(workerParams)
|
||||||
|
@ -84,7 +88,8 @@ internal class SasTransportRoomMessage(
|
||||||
// }
|
// }
|
||||||
// }, listenerExecutor)
|
// }, listenerExecutor)
|
||||||
|
|
||||||
val workLiveData = WorkManager.getInstance(context).getWorkInfosForUniqueWorkLiveData("${roomId}_VerificationWork")
|
val workLiveData = workManagerProvider.workManager
|
||||||
|
.getWorkInfosForUniqueWorkLiveData("${roomId}_VerificationWork")
|
||||||
|
|
||||||
val observer = object : Observer<List<WorkInfo>> {
|
val observer = object : Observer<List<WorkInfo>> {
|
||||||
override fun onChanged(workInfoList: 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) {
|
callback: (String?, MessageVerificationRequestContent?) -> Unit) {
|
||||||
val info = MessageVerificationRequestContent(
|
val info = MessageVerificationRequestContent(
|
||||||
body = context.getString(R.string.key_verification_request_fallback_message, userId),
|
body = stringProvider.getString(R.string.key_verification_request_fallback_message, userId),
|
||||||
fromDevice = userDevice,
|
fromDevice = userDeviceId ?: "",
|
||||||
toUserId = otherUserId,
|
toUserId = otherUserId,
|
||||||
methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS)
|
methods = supportedMethods
|
||||||
)
|
)
|
||||||
val content = info.toContent()
|
val content = info.toContent()
|
||||||
|
|
||||||
|
@ -131,24 +139,25 @@ internal class SasTransportRoomMessage(
|
||||||
)
|
)
|
||||||
|
|
||||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||||
userId = userId,
|
sessionId = sessionId,
|
||||||
event = event
|
event = event
|
||||||
))
|
))
|
||||||
|
|
||||||
val workRequest = WorkManagerUtil.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.setInputData(workerParams)
|
.setInputData(workerParams)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance(context)
|
workManagerProvider.workManager
|
||||||
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
|
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
|
||||||
.enqueue()
|
.enqueue()
|
||||||
|
|
||||||
// I cannot just listen to the given work request, because when used in a uniqueWork,
|
// I cannot just listen to the given work request, because when used in a uniqueWork,
|
||||||
// The callback is called while it is still Running ...
|
// 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>> {
|
val observer = object : Observer<List<WorkInfo>> {
|
||||||
override fun onChanged(workInfoList: 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")
|
Timber.d("## SAS canceling transaction $transactionId for reason $code")
|
||||||
val event = createEventAndLocalEcho(
|
val event = createEventAndLocalEcho(
|
||||||
type = EventType.KEY_VERIFICATION_CANCEL,
|
type = EventType.KEY_VERIFICATION_CANCEL,
|
||||||
|
@ -182,7 +191,7 @@ internal class SasTransportRoomMessage(
|
||||||
content = MessageVerificationCancelContent.create(transactionId, code).toContent()
|
content = MessageVerificationCancelContent.create(transactionId, code).toContent()
|
||||||
)
|
)
|
||||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||||
userId = this.userId,
|
sessionId = sessionId,
|
||||||
event = event
|
event = event
|
||||||
))
|
))
|
||||||
enqueueSendWork(workerParams)
|
enqueueSendWork(workerParams)
|
||||||
|
@ -200,19 +209,19 @@ internal class SasTransportRoomMessage(
|
||||||
).toContent()
|
).toContent()
|
||||||
)
|
)
|
||||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||||
userId = userId,
|
sessionId = sessionId,
|
||||||
event = event
|
event = event
|
||||||
))
|
))
|
||||||
enqueueSendWork(workerParams)
|
enqueueSendWork(workerParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enqueueSendWork(workerParams: Data): Pair<Operation, UUID> {
|
private fun enqueueSendWork(workerParams: Data): Pair<Operation, UUID> {
|
||||||
val workRequest = WorkManagerUtil.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.setInputData(workerParams)
|
.setInputData(workerParams)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.build()
|
||||||
return WorkManager.getInstance(context)
|
return workManagerProvider.workManager
|
||||||
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
|
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
|
||||||
.enqueue() to workRequest.id
|
.enqueue() to workRequest.id
|
||||||
}
|
}
|
||||||
|
@ -284,12 +293,18 @@ internal class SasTransportRoomMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class SasTransportRoomMessageFactory @Inject constructor(
|
internal class SasTransportRoomMessageFactory @Inject constructor(
|
||||||
private val context: Context,
|
private val workManagerProvider: WorkManagerProvider,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
private val monarchy: Monarchy,
|
private val monarchy: Monarchy,
|
||||||
|
@SessionId
|
||||||
|
private val sessionId: String,
|
||||||
|
@UserId
|
||||||
|
private val userId: String,
|
||||||
|
@DeviceId
|
||||||
|
private val deviceId: String?,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory) {
|
private val localEchoEventFactory: LocalEchoEventFactory) {
|
||||||
|
|
||||||
fun createTransport(userId: String, userDevice: String, roomId: String, tx: SASVerificationTransaction?
|
fun createTransport(roomId: String, tx: SASVerificationTransaction?): SasTransportRoomMessage {
|
||||||
): SasTransportRoomMessage {
|
return SasTransportRoomMessage(workManagerProvider, stringProvider, sessionId, userId, deviceId, roomId, monarchy, localEchoEventFactory, tx)
|
||||||
return SasTransportRoomMessage(context, userId, userDevice, roomId, monarchy, localEchoEventFactory, tx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,10 @@ internal class SasTransportToDevice(
|
||||||
private var taskExecutor: TaskExecutor
|
private var taskExecutor: TaskExecutor
|
||||||
) : SasTransport {
|
) : 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) {
|
callback: (String?, MessageVerificationRequestContent?) -> Unit) {
|
||||||
// TODO "not implemented"
|
// TODO "not implemented"
|
||||||
}
|
}
|
||||||
|
@ -78,11 +81,11 @@ internal class SasTransportToDevice(
|
||||||
// To device do not do anything here
|
// 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")
|
Timber.d("## SAS canceling transaction $transactionId for reason $code")
|
||||||
val cancelMessage = KeyVerificationCancel.create(transactionId, code)
|
val cancelMessage = KeyVerificationCancel.create(transactionId, code)
|
||||||
val contentMap = MXUsersDevicesMap<Any>()
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
contentMap.setObject(otherUserId, otherUserDevice, cancelMessage)
|
contentMap.setObject(otherUserId, otherUserDeviceId, cancelMessage)
|
||||||
sendToDeviceTask
|
sendToDeviceTask
|
||||||
.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) {
|
.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) {
|
||||||
this.callback = object : MatrixCallback<Unit> {
|
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.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
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.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.WorkerParamsFactory
|
||||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -34,9 +35,10 @@ internal class SendVerificationMessageWorker constructor(context: Context, param
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
val userId: String,
|
override val sessionId: String,
|
||||||
val event: Event
|
val event: Event,
|
||||||
)
|
override val lastFailureMessage: String? = null
|
||||||
|
) : SessionWorkerParams
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var sendVerificationMessageTask: SendVerificationMessageTask
|
lateinit var sendVerificationMessageTask: SendVerificationMessageTask
|
||||||
|
@ -49,10 +51,10 @@ internal class SendVerificationMessageWorker constructor(context: Context, param
|
||||||
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||||
?: return Result.success(errorOutputData)
|
?: return Result.success(errorOutputData)
|
||||||
|
|
||||||
val sessionComponent = getSessionComponent(params.userId)
|
val sessionComponent = getSessionComponent(params.sessionId)
|
||||||
?: return Result.success(errorOutputData).also {
|
?: return Result.success(errorOutputData).also {
|
||||||
// TODO, can this happen? should I update local echo?
|
// 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)
|
sessionComponent.inject(this)
|
||||||
val localId = params.event.eventId ?: ""
|
val localId = params.event.eventId ?: ""
|
||||||
|
|
|
@ -26,7 +26,7 @@ import javax.inject.Qualifier
|
||||||
internal annotation class UserId
|
internal annotation class UserId
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to inject the userId
|
* Used to inject the deviceId
|
||||||
*/
|
*/
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@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
|
package im.vector.matrix.android.internal.session
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import dagger.Lazy
|
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.crypto.DefaultCryptoService
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.di.SessionId
|
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.SyncTaskSequencer
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||||
import im.vector.matrix.android.internal.session.sync.job.SyncThread
|
import im.vector.matrix.android.internal.session.sync.job.SyncThread
|
||||||
|
@ -63,7 +63,7 @@ import javax.inject.Provider
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class DefaultSession @Inject constructor(
|
internal class DefaultSession @Inject constructor(
|
||||||
override val sessionParams: SessionParams,
|
override val sessionParams: SessionParams,
|
||||||
private val context: Context,
|
private val workManagerProvider: WorkManagerProvider,
|
||||||
private val eventBus: EventBus,
|
private val eventBus: EventBus,
|
||||||
@SessionId
|
@SessionId
|
||||||
override val sessionId: String,
|
override val sessionId: String,
|
||||||
|
@ -122,15 +122,15 @@ internal class DefaultSession @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requireBackgroundSync() {
|
override fun requireBackgroundSync() {
|
||||||
SyncWorker.requireBackgroundSync(context, sessionId)
|
SyncWorker.requireBackgroundSync(workManagerProvider, sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startAutomaticBackgroundSync(repeatDelay: Long) {
|
override fun startAutomaticBackgroundSync(repeatDelay: Long) {
|
||||||
SyncWorker.automaticallyBackgroundSync(context, sessionId, 0, repeatDelay)
|
SyncWorker.automaticallyBackgroundSync(workManagerProvider, sessionId, 0, repeatDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopAnyBackgroundSync() {
|
override fun stopAnyBackgroundSync() {
|
||||||
SyncWorker.stopAnyBackgroundSync(context)
|
SyncWorker.stopAnyBackgroundSync(workManagerProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startSync(fromForeground: Boolean) {
|
override fun startSync(fromForeground: Boolean) {
|
||||||
|
|
|
@ -16,9 +16,7 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.session.group
|
package im.vector.matrix.android.internal.session.group
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import androidx.work.WorkManager
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
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.model.GroupSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.di.SessionId
|
import im.vector.matrix.android.internal.di.SessionId
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
|
||||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import io.realm.OrderedCollectionChangeSet
|
import io.realm.OrderedCollectionChangeSet
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
|
@ -38,7 +35,7 @@ import javax.inject.Inject
|
||||||
private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
|
private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
|
||||||
|
|
||||||
internal class GroupSummaryUpdater @Inject constructor(
|
internal class GroupSummaryUpdater @Inject constructor(
|
||||||
private val context: Context,
|
private val workManagerProvider: WorkManagerProvider,
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
private val monarchy: Monarchy)
|
private val monarchy: Monarchy)
|
||||||
: RealmLiveEntityObserver<GroupEntity>(monarchy.realmConfiguration) {
|
: RealmLiveEntityObserver<GroupEntity>(monarchy.realmConfiguration) {
|
||||||
|
@ -72,12 +69,12 @@ internal class GroupSummaryUpdater @Inject constructor(
|
||||||
|
|
||||||
val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams)
|
val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams)
|
||||||
|
|
||||||
val sendWork = matrixOneTimeWorkRequestBuilder<GetGroupDataWorker>()
|
val sendWork = workManagerProvider.matrixOneTimeWorkRequestBuilder<GetGroupDataWorker>()
|
||||||
.setInputData(workData)
|
.setInputData(workData)
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance(context)
|
workManagerProvider.workManager
|
||||||
.beginUniqueWork(GET_GROUP_DATA_WORKER, ExistingWorkPolicy.APPEND, sendWork)
|
.beginUniqueWork(GET_GROUP_DATA_WORKER, ExistingWorkPolicy.APPEND, sendWork)
|
||||||
.enqueue()
|
.enqueue()
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.session.pushers
|
package im.vector.matrix.android.internal.session.pushers
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.work.BackoffPolicy
|
import androidx.work.BackoffPolicy
|
||||||
import androidx.work.WorkManager
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.pushers.Pusher
|
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.model.PusherEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.di.SessionId
|
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.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
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 im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultPusherService @Inject constructor(
|
internal class DefaultPusherService @Inject constructor(
|
||||||
private val context: Context,
|
private val workManagerProvider: WorkManagerProvider,
|
||||||
private val monarchy: Monarchy,
|
private val monarchy: Monarchy,
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
private val getPusherTask: GetPushersTask,
|
private val getPusherTask: GetPushersTask,
|
||||||
|
@ -68,12 +65,12 @@ internal class DefaultPusherService @Inject constructor(
|
||||||
|
|
||||||
val params = AddHttpPusherWorker.Params(sessionId, pusher)
|
val params = AddHttpPusherWorker.Params(sessionId, pusher)
|
||||||
|
|
||||||
val request = matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
|
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.setInputData(WorkerParamsFactory.toData(params))
|
.setInputData(WorkerParamsFactory.toData(params))
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.build()
|
||||||
WorkManager.getInstance(context).enqueue(request)
|
workManagerProvider.workManager.enqueue(request)
|
||||||
return request.id
|
return request.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.session.room.relation
|
package im.vector.matrix.android.internal.session.room.relation
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import androidx.work.OneTimeWorkRequest
|
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.session.room.timeline.TimelineSendEventWorkCommon
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
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.util.fetchCopyMap
|
||||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
internal class DefaultRelationService @AssistedInject constructor(
|
internal class DefaultRelationService @AssistedInject constructor(
|
||||||
@Assisted private val roomId: String,
|
@Assisted private val roomId: String,
|
||||||
private val context: Context,
|
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
|
private val timeLineSendEventWorkCommon: TimelineSendEventWorkCommon,
|
||||||
private val eventFactory: LocalEchoEventFactory,
|
private val eventFactory: LocalEchoEventFactory,
|
||||||
private val cryptoService: CryptoService,
|
private val cryptoService: CryptoService,
|
||||||
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
||||||
|
@ -85,8 +83,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
|
val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
|
||||||
.also { saveLocalEcho(it) }
|
.also { saveLocalEcho(it) }
|
||||||
val sendRelationWork = createSendEventWork(event, true)
|
val sendRelationWork = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork)
|
timeLineSendEventWorkCommon.postWork(roomId, sendRelationWork)
|
||||||
CancelableWork(context, sendRelationWork.id)
|
|
||||||
} else {
|
} else {
|
||||||
Timber.w("Reaction already added")
|
Timber.w("Reaction already added")
|
||||||
NoOpCancellable
|
NoOpCancellable
|
||||||
|
@ -111,7 +108,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
.also { saveLocalEcho(it) }
|
.also { saveLocalEcho(it) }
|
||||||
val redactWork = createRedactEventWork(redactEvent, toRedact, null)
|
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,
|
eventId,
|
||||||
reason)
|
reason)
|
||||||
val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
return timeLineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun editTextMessage(targetEventId: String,
|
override fun editTextMessage(targetEventId: String,
|
||||||
|
@ -146,12 +143,10 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
return if (cryptoService.isRoomEncrypted(roomId)) {
|
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||||
val workRequest = createSendEventWork(event, false)
|
val workRequest = createSendEventWork(event, false)
|
||||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
timeLineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, workRequest)
|
||||||
CancelableWork(context, encryptWork.id)
|
|
||||||
} else {
|
} else {
|
||||||
val workRequest = createSendEventWork(event, true)
|
val workRequest = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
timeLineSendEventWorkCommon.postWork(roomId, workRequest)
|
||||||
CancelableWork(context, workRequest.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,12 +163,10 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
return if (cryptoService.isRoomEncrypted(roomId)) {
|
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||||
val workRequest = createSendEventWork(event, false)
|
val workRequest = createSendEventWork(event, false)
|
||||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
timeLineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, workRequest)
|
||||||
CancelableWork(context, encryptWork.id)
|
|
||||||
} else {
|
} else {
|
||||||
val workRequest = createSendEventWork(event, true)
|
val workRequest = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
timeLineSendEventWorkCommon.postWork(roomId, workRequest)
|
||||||
CancelableWork(context, workRequest.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,12 +187,10 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
return if (cryptoService.isRoomEncrypted(roomId)) {
|
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||||
val workRequest = createSendEventWork(event, false)
|
val workRequest = createSendEventWork(event, false)
|
||||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
timeLineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, workRequest)
|
||||||
CancelableWork(context, encryptWork.id)
|
|
||||||
} else {
|
} else {
|
||||||
val workRequest = createSendEventWork(event, true)
|
val workRequest = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
timeLineSendEventWorkCommon.postWork(roomId, workRequest)
|
||||||
CancelableWork(context, workRequest.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,13 +198,13 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
// Same parameter
|
// Same parameter
|
||||||
val params = EncryptEventWorker.Params(sessionId, roomId, event, keepKeys)
|
val params = EncryptEventWorker.Params(sessionId, roomId, event, keepKeys)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(params)
|
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 {
|
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event)
|
val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
return timeLineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getEventAnnotationsSummary(eventId: String): EventAnnotationsSummary? {
|
override fun getEventAnnotationsSummary(eventId: String): EventAnnotationsSummary? {
|
||||||
|
|
|
@ -16,8 +16,10 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.session.room.send
|
package im.vector.matrix.android.internal.session.room.send
|
||||||
|
|
||||||
import android.content.Context
|
import androidx.work.BackoffPolicy
|
||||||
import androidx.work.*
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.OneTimeWorkRequest
|
||||||
|
import androidx.work.Operation
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import com.zhuinden.monarchy.Monarchy
|
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.findAllInRoomWithSendStates
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.di.SessionId
|
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.content.UploadContentWorker
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
|
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
|
||||||
import im.vector.matrix.android.internal.util.CancelableWork
|
import im.vector.matrix.android.internal.util.CancelableWork
|
||||||
import im.vector.matrix.android.internal.worker.AlwaysSuccessfulWorker
|
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.WorkerParamsFactory
|
||||||
import im.vector.matrix.android.internal.worker.startChain
|
import im.vector.matrix.android.internal.worker.startChain
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -55,7 +56,8 @@ private const val BACKOFF_DELAY = 10_000L
|
||||||
|
|
||||||
internal class DefaultSendService @AssistedInject constructor(
|
internal class DefaultSendService @AssistedInject constructor(
|
||||||
@Assisted private val roomId: String,
|
@Assisted private val roomId: String,
|
||||||
private val context: Context,
|
private val workManagerProvider: WorkManagerProvider,
|
||||||
|
private val timelineSendEventWorkCommon: TimelineSendEventWorkCommon,
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val cryptoService: CryptoService,
|
private val cryptoService: CryptoService,
|
||||||
|
@ -91,12 +93,10 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
Timber.v("Send event in encrypted room")
|
Timber.v("Send event in encrypted room")
|
||||||
val encryptWork = createEncryptEventWork(event, true)
|
val encryptWork = createEncryptEventWork(event, true)
|
||||||
val sendWork = createSendEventWork(event, false)
|
val sendWork = createSendEventWork(event, false)
|
||||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, sendWork)
|
timelineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, sendWork)
|
||||||
CancelableWork(context, encryptWork.id)
|
|
||||||
} else {
|
} else {
|
||||||
val sendWork = createSendEventWork(event, true)
|
val sendWork = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, sendWork)
|
timelineSendEventWorkCommon.postWork(roomId, sendWork)
|
||||||
CancelableWork(context, sendWork.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,8 +109,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
override fun redactEvent(event: Event, reason: String?): Cancelable {
|
override fun redactEvent(event: Event, reason: String?): Cancelable {
|
||||||
// TODO manage media/attachements?
|
// TODO manage media/attachements?
|
||||||
val redactWork = createRedactEventWork(event, reason)
|
val redactWork = createRedactEventWork(event, reason)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, redactWork)
|
return timelineSendEventWorkCommon.postWork(roomId, redactWork)
|
||||||
return CancelableWork(context, redactWork.id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? {
|
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? {
|
||||||
|
@ -168,16 +167,16 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearSendingQueue() {
|
override fun clearSendingQueue() {
|
||||||
TimelineSendEventWorkCommon.cancelAllWorks(context, roomId)
|
timelineSendEventWorkCommon.cancelAllWorks(roomId)
|
||||||
WorkManager.getInstance(context).cancelUniqueWork(buildWorkName(UPLOAD_WORK))
|
workManagerProvider.workManager.cancelUniqueWork(buildWorkName(UPLOAD_WORK))
|
||||||
|
|
||||||
// Replace the worker chains with a AlwaysSuccessfulWorker, to ensure the queues are well emptied
|
// Replace the worker chains with a AlwaysSuccessfulWorker, to ensure the queues are well emptied
|
||||||
matrixOneTimeWorkRequestBuilder<AlwaysSuccessfulWorker>()
|
workManagerProvider.matrixOneTimeWorkRequestBuilder<AlwaysSuccessfulWorker>()
|
||||||
.build().let {
|
.build().let {
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, it, ExistingWorkPolicy.REPLACE)
|
timelineSendEventWorkCommon.postWork(roomId, it, ExistingWorkPolicy.REPLACE)
|
||||||
|
|
||||||
// need to clear also image sending queue
|
// need to clear also image sending queue
|
||||||
WorkManager.getInstance(context)
|
workManagerProvider.workManager
|
||||||
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it)
|
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it)
|
||||||
.enqueue()
|
.enqueue()
|
||||||
}
|
}
|
||||||
|
@ -245,7 +244,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
return internalSendMedia(event, attachment)
|
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 isRoomEncrypted = cryptoService.isRoomEncrypted(roomId)
|
||||||
|
|
||||||
val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true)
|
val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true)
|
||||||
|
@ -254,7 +253,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
if (isRoomEncrypted) {
|
if (isRoomEncrypted) {
|
||||||
val encryptWork = createEncryptEventWork(localEcho, false /*not start of chain, take input error*/)
|
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)
|
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
||||||
.then(encryptWork)
|
.then(encryptWork)
|
||||||
.then(sendWork)
|
.then(sendWork)
|
||||||
|
@ -267,13 +266,13 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}, workerFutureListenerExecutor)
|
}, workerFutureListenerExecutor)
|
||||||
} else {
|
} else {
|
||||||
WorkManager.getInstance(context)
|
workManagerProvider.workManager
|
||||||
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
||||||
.then(sendWork)
|
.then(sendWork)
|
||||||
.enqueue()
|
.enqueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
return CancelableWork(context, sendWork.id)
|
return CancelableWork(workManagerProvider.workManager, sendWork.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveLocalEcho(event: Event) {
|
private fun saveLocalEcho(event: Event) {
|
||||||
|
@ -289,8 +288,8 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
val params = EncryptEventWorker.Params(sessionId, roomId, event)
|
val params = EncryptEventWorker.Params(sessionId, roomId, event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(params)
|
val sendWorkData = WorkerParamsFactory.toData(params)
|
||||||
|
|
||||||
return matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
|
return workManagerProvider.matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.setInputData(sendWorkData)
|
.setInputData(sendWorkData)
|
||||||
.startChain(startChain)
|
.startChain(startChain)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||||
|
@ -301,7 +300,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event)
|
val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
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 {
|
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 sendContentWorkerParams = RedactEventWorker.Params(sessionId, redactEvent.eventId!!, roomId, event.eventId, reason)
|
||||||
val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
return timelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createUploadMediaWork(event: Event,
|
private fun createUploadMediaWork(event: Event,
|
||||||
|
@ -320,8 +319,8 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, roomId, event, attachment, isRoomEncrypted)
|
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, roomId, event, attachment, isRoomEncrypted)
|
||||||
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)
|
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)
|
||||||
|
|
||||||
return matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
return workManagerProvider.matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.startChain(startChain)
|
.startChain(startChain)
|
||||||
.setInputData(uploadWorkData)
|
.setInputData(uploadWorkData)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||||
|
|
|
@ -15,51 +15,54 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.session.room.timeline
|
package im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
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 im.vector.matrix.android.internal.worker.startChain
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
private const val SEND_WORK = "SEND_WORK"
|
|
||||||
private const val BACKOFF_DELAY = 10_000L
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for sending event related works.
|
* Helper class for sending event related works.
|
||||||
* All send event from a room are using the same workchain, in order to ensure order.
|
* 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)
|
* 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.
|
* 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) {
|
fun postSequentialWorks(roomId: String, vararg workRequests: OneTimeWorkRequest): Cancelable {
|
||||||
when {
|
return when {
|
||||||
workRequests.isEmpty() -> return
|
workRequests.isEmpty() -> NoOpCancellable
|
||||||
workRequests.size == 1 -> postWork(context, roomId, workRequests.first())
|
workRequests.size == 1 -> postWork(roomId, workRequests.first())
|
||||||
else -> {
|
else -> {
|
||||||
val firstWork = workRequests.first()
|
val firstWork = workRequests.first()
|
||||||
var continuation = WorkManager.getInstance(context)
|
var continuation = workManagerProvider.workManager
|
||||||
.beginUniqueWork(buildWorkName(roomId), ExistingWorkPolicy.APPEND, firstWork)
|
.beginUniqueWork(buildWorkName(roomId), ExistingWorkPolicy.APPEND, firstWork)
|
||||||
for (i in 1 until workRequests.size) {
|
for (i in 1 until workRequests.size) {
|
||||||
val workRequest = workRequests[i]
|
val workRequest = workRequests[i]
|
||||||
continuation = continuation.then(workRequest)
|
continuation = continuation.then(workRequest)
|
||||||
}
|
}
|
||||||
continuation.enqueue()
|
continuation.enqueue()
|
||||||
|
CancelableWork(workManagerProvider.workManager, firstWork.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun postWork(context: Context, roomId: String, workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND) {
|
fun postWork(roomId: String, workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND): Cancelable {
|
||||||
WorkManager.getInstance(context)
|
workManagerProvider.workManager
|
||||||
.beginUniqueWork(buildWorkName(roomId), policy, workRequest)
|
.beginUniqueWork(buildWorkName(roomId), policy, workRequest)
|
||||||
.enqueue()
|
.enqueue()
|
||||||
|
|
||||||
|
return CancelableWork(workManagerProvider.workManager, workRequest.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified W : ListenableWorker> createWork(data: Data, startChain: Boolean): OneTimeWorkRequest {
|
inline fun <reified W : ListenableWorker> createWork(data: Data, startChain: Boolean): OneTimeWorkRequest {
|
||||||
return matrixOneTimeWorkRequestBuilder<W>()
|
return workManagerProvider.matrixOneTimeWorkRequestBuilder<W>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.startChain(startChain)
|
.startChain(startChain)
|
||||||
.setInputData(data)
|
.setInputData(data)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||||
|
@ -70,7 +73,13 @@ internal object TimelineSendEventWorkCommon {
|
||||||
return "${roomId}_$SEND_WORK"
|
return "${roomId}_$SEND_WORK"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelAllWorks(context: Context, roomId: String) {
|
fun cancelAllWorks(roomId: String) {
|
||||||
WorkManager.getInstance(context).cancelUniqueWork(buildWorkName(roomId))
|
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
|
package im.vector.matrix.android.internal.session.signout
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import im.vector.matrix.android.BuildConfig
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.failure.MatrixError
|
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.SessionModule
|
||||||
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
|
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
@ -45,7 +43,7 @@ internal interface SignOutTask : Task<SignOutTask.Params, Unit> {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultSignOutTask @Inject constructor(
|
internal class DefaultSignOutTask @Inject constructor(
|
||||||
private val context: Context,
|
private val workManagerProvider: WorkManagerProvider,
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
private val signOutAPI: SignOutAPI,
|
private val signOutAPI: SignOutAPI,
|
||||||
private val sessionManager: SessionManager,
|
private val sessionManager: SessionManager,
|
||||||
|
@ -87,7 +85,7 @@ internal class DefaultSignOutTask @Inject constructor(
|
||||||
sessionManager.releaseSession(sessionId)
|
sessionManager.releaseSession(sessionId)
|
||||||
|
|
||||||
Timber.d("SignOut: cancel pending works...")
|
Timber.d("SignOut: cancel pending works...")
|
||||||
WorkManagerUtil.cancelAllWorks(context)
|
workManagerProvider.cancelAllWorks()
|
||||||
|
|
||||||
Timber.d("SignOut: delete session params...")
|
Timber.d("SignOut: delete session params...")
|
||||||
sessionParamsStore.delete(sessionId)
|
sessionParamsStore.delete(sessionId)
|
||||||
|
|
|
@ -16,15 +16,17 @@
|
||||||
package im.vector.matrix.android.internal.session.sync.job
|
package im.vector.matrix.android.internal.session.sync.job
|
||||||
|
|
||||||
import android.content.Context
|
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 com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.failure.isTokenError
|
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.network.NetworkConnectivityChecker
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTask
|
import im.vector.matrix.android.internal.session.sync.SyncTask
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
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.WorkerParamsFactory
|
||||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -75,30 +77,33 @@ internal class SyncWorker(context: Context,
|
||||||
|
|
||||||
companion object {
|
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 data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, false))
|
||||||
val workRequest = matrixOneTimeWorkRequestBuilder<SyncWorker>()
|
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 1_000, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, 1_000, TimeUnit.MILLISECONDS)
|
||||||
.setInputData(data)
|
.setInputData(data)
|
||||||
.build()
|
.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 data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, true))
|
||||||
val workRequest = matrixOneTimeWorkRequestBuilder<SyncWorker>()
|
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>()
|
||||||
.setConstraints(WorkManagerUtil.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.setInputData(data)
|
.setInputData(data)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, delay, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, delay, TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.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) {
|
fun stopAnyBackgroundSync(workManagerProvider: WorkManagerProvider) {
|
||||||
WorkManager.getInstance(context).cancelUniqueWork(BG_SYNC_WORK_NAME)
|
workManagerProvider.workManager
|
||||||
|
.cancelUniqueWork(BG_SYNC_WORK_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,14 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.util
|
package im.vector.matrix.android.internal.util
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
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 {
|
private val workId: UUID) : Cancelable {
|
||||||
|
|
||||||
override fun cancel() {
|
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:label="@string/title_activity_settings"
|
||||||
android:windowSoftInputMode="adjustResize" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
<activity android:name=".features.media.VideoMediaViewerActivity" />
|
<activity android:name=".features.media.VideoMediaViewerActivity" />
|
||||||
<activity
|
|
||||||
android:name=".features.crypto.verification.SASVerificationActivity"
|
|
||||||
android:label="@string/title_activity_verify_device" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".features.crypto.keysbackup.restore.KeysBackupRestoreActivity"
|
android:name=".features.crypto.keysbackup.restore.KeysBackupRestoreActivity"
|
||||||
android:label="@string/title_activity_keys_backup_setup" />
|
android:label="@string/title_activity_keys_backup_setup" />
|
||||||
|
|
|
@ -23,7 +23,10 @@ import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.multibindings.IntoMap
|
import dagger.multibindings.IntoMap
|
||||||
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
|
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
|
||||||
import im.vector.riotx.features.crypto.verification.*
|
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.HomeDetailFragment
|
||||||
import im.vector.riotx.features.home.HomeDrawerFragment
|
import im.vector.riotx.features.home.HomeDrawerFragment
|
||||||
import im.vector.riotx.features.home.LoadingFragment
|
import im.vector.riotx.features.home.LoadingFragment
|
||||||
|
@ -239,26 +242,6 @@ interface FragmentModule {
|
||||||
@FragmentKey(VectorSettingsDevicesFragment::class)
|
@FragmentKey(VectorSettingsDevicesFragment::class)
|
||||||
fun bindVectorSettingsDevicesFragment(fragment: VectorSettingsDevicesFragment): Fragment
|
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
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(PublicRoomsFragment::class)
|
@FragmentKey(PublicRoomsFragment::class)
|
||||||
|
@ -302,15 +285,15 @@ interface FragmentModule {
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(VerificationChooseMethodFragment::class)
|
@FragmentKey(VerificationChooseMethodFragment::class)
|
||||||
fun bindVerificationMethodChooserFragment(fragment: VerificationChooseMethodFragment): Fragment
|
fun bindVerificationChooseMethodFragment(fragment: VerificationChooseMethodFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(SASVerificationCodeFragment::class)
|
@FragmentKey(VerificationEmojiCodeFragment::class)
|
||||||
fun bindVerificationSasCodeFragment(fragment: SASVerificationCodeFragment): Fragment
|
fun bindVerificationEmojiCodeFragment(fragment: VerificationEmojiCodeFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(VerificationConclusionFragment::class)
|
@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.KeysBackupRestoreFromPassphraseViewModel
|
||||||
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||||
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
|
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.HomeSharedActionViewModel
|
||||||
import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel
|
import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.action.MessageSharedActionViewModel
|
import im.vector.riotx.features.home.room.detail.timeline.action.MessageSharedActionViewModel
|
||||||
|
@ -61,11 +60,6 @@ interface ViewModelModule {
|
||||||
@ViewModelKey(EmojiChooserViewModel::class)
|
@ViewModelKey(EmojiChooserViewModel::class)
|
||||||
fun bindEmojiChooserViewModel(viewModel: EmojiChooserViewModel): ViewModel
|
fun bindEmojiChooserViewModel(viewModel: EmojiChooserViewModel): ViewModel
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@ViewModelKey(SasVerificationViewModel::class)
|
|
||||||
fun bindSasVerificationViewModel(viewModel: SasVerificationViewModel): ViewModel
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@ViewModelKey(KeysBackupRestoreFromKeyViewModel::class)
|
@ViewModelKey(KeysBackupRestoreFromKeyViewModel::class)
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.riotx.core.epoxy
|
||||||
|
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -33,6 +34,7 @@ abstract class ErrorWithRetryItem : VectorEpoxyModel<ErrorWithRetryItem.Holder>(
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
holder.textView.text = text
|
holder.textView.text = text
|
||||||
|
holder.buttonView.isVisible = listener != null
|
||||||
holder.buttonView.setOnClickListener { listener?.invoke() }
|
holder.buttonView.setOnClickListener { listener?.invoke() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,12 +28,17 @@ fun RecyclerView.configureWith(epoxyController: EpoxyController,
|
||||||
itemAnimator: RecyclerView.ItemAnimator? = null,
|
itemAnimator: RecyclerView.ItemAnimator? = null,
|
||||||
viewPool: RecyclerView.RecycledViewPool? = null,
|
viewPool: RecyclerView.RecycledViewPool? = null,
|
||||||
showDivider: Boolean = false,
|
showDivider: Boolean = false,
|
||||||
hasFixedSize: Boolean = true) {
|
hasFixedSize: Boolean = true,
|
||||||
|
disableItemAnimation: Boolean = false) {
|
||||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false).apply {
|
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false).apply {
|
||||||
recycleChildrenOnDetach = viewPool != null
|
recycleChildrenOnDetach = viewPool != null
|
||||||
}
|
}
|
||||||
setRecycledViewPool(viewPool)
|
setRecycledViewPool(viewPool)
|
||||||
itemAnimator?.let { this.itemAnimator = it }
|
if (disableItemAnimation) {
|
||||||
|
this.itemAnimator = null
|
||||||
|
} else {
|
||||||
|
itemAnimator?.let { this.itemAnimator = it }
|
||||||
|
}
|
||||||
if (showDivider) {
|
if (showDivider) {
|
||||||
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
|
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,9 +102,11 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
|
||||||
private lateinit var sessionListener: SessionListener
|
private lateinit var sessionListener: SessionListener
|
||||||
protected lateinit var bugReporter: BugReporter
|
protected lateinit var bugReporter: BugReporter
|
||||||
lateinit var rageShake: RageShake
|
lateinit var rageShake: RageShake
|
||||||
|
|
||||||
|
lateinit var navigator: Navigator
|
||||||
private set
|
private set
|
||||||
protected lateinit var navigator: Navigator
|
|
||||||
private lateinit var fragmentFactory: FragmentFactory
|
private lateinit var fragmentFactory: FragmentFactory
|
||||||
|
|
||||||
private lateinit var activeSessionHolder: ActiveSessionHolder
|
private lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
private lateinit var vectorPreferences: VectorPreferences
|
private lateinit var vectorPreferences: VectorPreferences
|
||||||
|
|
||||||
|
@ -210,8 +212,8 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
|
||||||
handleInvalidToken(globalError)
|
handleInvalidToken(globalError)
|
||||||
is GlobalError.ConsentNotGivenError ->
|
is GlobalError.ConsentNotGivenError ->
|
||||||
consentNotGivenHelper.displayDialog(globalError.consentUri,
|
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.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
|
import butterknife.ButterKnife
|
||||||
|
import butterknife.Unbinder
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.MvRxView
|
import com.airbnb.mvrx.MvRxView
|
||||||
import com.airbnb.mvrx.MvRxViewId
|
import com.airbnb.mvrx.MvRxViewId
|
||||||
|
@ -43,6 +49,15 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
|
||||||
final override val mvrxViewId: String by mvrxViewIdProperty
|
final override val mvrxViewId: String by mvrxViewIdProperty
|
||||||
private lateinit var screenComponent: ScreenComponent
|
private lateinit var screenComponent: ScreenComponent
|
||||||
|
|
||||||
|
/* ==========================================================================================
|
||||||
|
* View
|
||||||
|
* ========================================================================================== */
|
||||||
|
|
||||||
|
@LayoutRes
|
||||||
|
abstract fun getLayoutResId(): Int
|
||||||
|
|
||||||
|
private var unBinder: Unbinder? = null
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* View model
|
* View model
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
@ -67,6 +82,18 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
|
||||||
|
|
||||||
open val showExpanded = false
|
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) {
|
override fun onAttach(context: Context) {
|
||||||
screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
|
screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
|
||||||
viewModelFactory = screenComponent.viewModelFactory()
|
viewModelFactory = screenComponent.viewModelFactory()
|
||||||
|
|
|
@ -98,10 +98,9 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
// Don't activate animation as we might have way to much item animation when filtering
|
|
||||||
recyclerView.itemAnimator = null
|
|
||||||
knownUsersController.callback = this
|
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() {
|
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.DeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.features.crypto.verification.SASVerificationActivity
|
|
||||||
import im.vector.riotx.features.popup.PopupAlertManager
|
import im.vector.riotx.features.popup.PopupAlertManager
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.*
|
||||||
import java.util.Date
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
@ -195,18 +193,19 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
|
||||||
denyAllRequests(mappingKey)
|
denyAllRequests(mappingKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
alert.addButton(
|
// TODO send to the new profile page
|
||||||
context.getString(R.string.start_verification_short_label),
|
// alert.addButton(
|
||||||
Runnable {
|
// context.getString(R.string.start_verification_short_label),
|
||||||
alert.weakCurrentActivity?.get()?.let {
|
// Runnable {
|
||||||
val intent = SASVerificationActivity.outgoingIntent(it,
|
// alert.weakCurrentActivity?.get()?.let {
|
||||||
session?.myUserId ?: "",
|
// val intent = SASVerificationActivity.outgoingIntent(it,
|
||||||
userId, deviceId)
|
// session?.myUserId ?: "",
|
||||||
it.startActivity(intent)
|
// userId, deviceId)
|
||||||
}
|
// it.startActivity(intent)
|
||||||
},
|
// }
|
||||||
false
|
// },
|
||||||
)
|
// false
|
||||||
|
// )
|
||||||
|
|
||||||
alert.addButton(context.getString(R.string.share_without_verifying_short_label), Runnable {
|
alert.addButton(context.getString(R.string.share_without_verifying_short_label), Runnable {
|
||||||
shareAllSessions(mappingKey)
|
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.SasVerificationService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
|
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.SasVerificationTxState
|
||||||
|
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
|
||||||
import im.vector.riotx.R
|
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.popup.PopupAlertManager
|
||||||
|
import im.vector.riotx.features.themes.ThemeUtils
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@ -48,46 +53,46 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
|
||||||
override fun transactionUpdated(tx: SasVerificationTransaction) {
|
override fun transactionUpdated(tx: SasVerificationTransaction) {
|
||||||
when (tx.state) {
|
when (tx.state) {
|
||||||
SasVerificationTxState.OnStarted -> {
|
SasVerificationTxState.OnStarted -> {
|
||||||
// Add a notification for every incoming request
|
// // Add a notification for every incoming request
|
||||||
val name = session?.getUser(tx.otherUserId)?.displayName
|
// val name = session?.getUser(tx.otherUserId)?.displayName
|
||||||
?: tx.otherUserId
|
// ?: tx.otherUserId
|
||||||
|
//
|
||||||
val alert = PopupAlertManager.VectorAlert(
|
// val alert = PopupAlertManager.VectorAlert(
|
||||||
"kvr_${tx.transactionId}",
|
// "kvr_${tx.transactionId}",
|
||||||
context.getString(R.string.sas_incoming_request_notif_title),
|
// context.getString(R.string.sas_incoming_request_notif_title),
|
||||||
context.getString(R.string.sas_incoming_request_notif_content, name),
|
// context.getString(R.string.sas_incoming_request_notif_content, name),
|
||||||
R.drawable.shield)
|
// R.drawable.shield)
|
||||||
.apply {
|
// .apply {
|
||||||
contentAction = Runnable {
|
// contentAction = Runnable {
|
||||||
val intent = SASVerificationActivity.incomingIntent(context,
|
// val intent = SASVerificationActivity.incomingIntent(context,
|
||||||
session?.myUserId ?: "",
|
// session?.myUserId ?: "",
|
||||||
tx.otherUserId,
|
// tx.otherUserId,
|
||||||
tx.transactionId)
|
// tx.transactionId)
|
||||||
weakCurrentActivity?.get()?.startActivity(intent)
|
// weakCurrentActivity?.get()?.startActivity(intent)
|
||||||
}
|
// }
|
||||||
dismissedAction = Runnable {
|
// dismissedAction = Runnable {
|
||||||
tx.cancel()
|
// tx.cancel()
|
||||||
}
|
// }
|
||||||
addButton(
|
// addButton(
|
||||||
context.getString(R.string.ignore),
|
// context.getString(R.string.ignore),
|
||||||
Runnable {
|
// Runnable {
|
||||||
tx.cancel()
|
// tx.cancel()
|
||||||
}
|
// }
|
||||||
)
|
// )
|
||||||
addButton(
|
// addButton(
|
||||||
context.getString(R.string.action_open),
|
// context.getString(R.string.action_open),
|
||||||
Runnable {
|
// Runnable {
|
||||||
val intent = SASVerificationActivity.incomingIntent(context,
|
// val intent = SASVerificationActivity.incomingIntent(context,
|
||||||
session?.myUserId ?: "",
|
// session?.myUserId ?: "",
|
||||||
tx.otherUserId,
|
// tx.otherUserId,
|
||||||
tx.transactionId)
|
// tx.transactionId)
|
||||||
weakCurrentActivity?.get()?.startActivity(intent)
|
// weakCurrentActivity?.get()?.startActivity(intent)
|
||||||
}
|
// }
|
||||||
)
|
// )
|
||||||
// 10mn expiration
|
// // 10mn expiration
|
||||||
expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L)
|
// expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L)
|
||||||
}
|
// }
|
||||||
PopupAlertManager.postVectorAlert(alert)
|
// PopupAlertManager.postVectorAlert(alert)
|
||||||
}
|
}
|
||||||
SasVerificationTxState.Cancelled,
|
SasVerificationTxState.Cancelled,
|
||||||
SasVerificationTxState.OnCancelled,
|
SasVerificationTxState.OnCancelled,
|
||||||
|
@ -101,4 +106,54 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
|
||||||
|
|
||||||
override fun markedAsManuallyVerified(userId: String, deviceId: String) {
|
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.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.transition.AutoTransition
|
import androidx.transition.AutoTransition
|
||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
|
||||||
import butterknife.Unbinder
|
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
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.di.ScreenComponent
|
||||||
import im.vector.riotx.core.extensions.commitTransactionNow
|
import im.vector.riotx.core.extensions.commitTransactionNow
|
||||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
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.home.AvatarRenderer
|
||||||
import im.vector.riotx.features.themes.ThemeUtils
|
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet_verification.*
|
import kotlinx.android.synthetic.main.bottom_sheet_verification.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -74,22 +73,13 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
@BindView(R.id.verificationRequestName)
|
@BindView(R.id.verificationRequestName)
|
||||||
lateinit var otherUserNameText: TextView
|
lateinit var otherUserNameText: TextView
|
||||||
|
|
||||||
|
@BindView(R.id.verificationRequestShield)
|
||||||
|
lateinit var otherUserShield: View
|
||||||
|
|
||||||
@BindView(R.id.verificationRequestAvatar)
|
@BindView(R.id.verificationRequestAvatar)
|
||||||
lateinit var otherUserAvatarImageView: ImageView
|
lateinit var otherUserAvatarImageView: ImageView
|
||||||
|
|
||||||
private var unBinder: Unbinder? = null
|
override fun getLayoutResId() = R.layout.bottom_sheet_verification
|
||||||
|
|
||||||
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 onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
@ -109,12 +99,15 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
it.otherUserMxItem?.let { matrixItem ->
|
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)
|
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?
|
// Did the request result in a SAS transaction?
|
||||||
|
@ -135,7 +128,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
SasVerificationTxState.SendingMac,
|
SasVerificationTxState.SendingMac,
|
||||||
SasVerificationTxState.MacSent,
|
SasVerificationTxState.MacSent,
|
||||||
SasVerificationTxState.Verifying -> {
|
SasVerificationTxState.Verifying -> {
|
||||||
showFragment(SASVerificationCodeFragment::class, Bundle().apply {
|
showFragment(VerificationEmojiCodeFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
||||||
it.otherUserMxItem?.id ?: "",
|
it.otherUserMxItem?.id ?: "",
|
||||||
it.pendingRequest?.transactionId))
|
it.pendingRequest?.transactionId))
|
||||||
|
|
|
@ -21,13 +21,9 @@ import com.airbnb.mvrx.*
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.session.Session
|
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.*
|
||||||
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.util.MatrixItem
|
import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.matrix.android.api.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
|
|
||||||
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
|
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
|
||||||
import im.vector.riotx.core.di.HasScreenInjector
|
import im.vector.riotx.core.di.HasScreenInjector
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
@ -108,7 +104,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
||||||
is VerificationAction.RequestVerificationByDM -> {
|
is VerificationAction.RequestVerificationByDM -> {
|
||||||
// session
|
// session
|
||||||
setState {
|
setState {
|
||||||
copy(pendingRequest = session.getSasVerificationService().requestKeyVerificationInDMs(otherUserId, roomId))
|
copy(pendingRequest = session.getSasVerificationService().requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, roomId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is VerificationAction.StartSASVerification -> {
|
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
|
val otherDevice = if (request.isIncoming) request.requestInfo?.fromDevice else request.readyInfo?.fromDevice
|
||||||
session.getSasVerificationService().beginKeyVerificationInDMs(
|
session.getSasVerificationService().beginKeyVerificationInDMs(
|
||||||
KeyVerificationStart.VERIF_METHOD_SAS,
|
VerificationMethod.SAS,
|
||||||
transactionId = action.pendingRequestTransactionId,
|
transactionId = action.pendingRequestTransactionId,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
otherUserId = request.otherUserId,
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxState
|
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.Session
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
|
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.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.matrix.android.internal.crypto.verification.PendingVerificationRequest
|
||||||
import im.vector.riotx.core.di.HasScreenInjector
|
import im.vector.riotx.core.di.HasScreenInjector
|
||||||
import im.vector.riotx.core.platform.EmptyAction
|
import im.vector.riotx.core.platform.EmptyAction
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
|
||||||
|
|
||||||
data class VerificationChooseMethodViewState(
|
data class VerificationChooseMethodViewState(
|
||||||
val otherUserId: String = "",
|
val otherUserId: String = "",
|
||||||
val transactionId: String = "",
|
val transactionId: String = "",
|
||||||
val QRModeAvailable: Boolean = false,
|
val QRModeAvailable: Boolean = false,
|
||||||
val SASMOdeAvailable: Boolean = false
|
val SASModeAvailable: Boolean = false
|
||||||
) : MvRxState
|
) : MvRxState
|
||||||
|
|
||||||
class VerificationChooseMethodViewModel @AssistedInject constructor(
|
class VerificationChooseMethodViewModel @AssistedInject constructor(
|
||||||
|
@ -48,15 +49,13 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
|
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
|
||||||
val pvr = session.getSasVerificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
|
val pvr = session.getSasVerificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
|
||||||
val qrAvailable = pvr?.readyInfo?.methods?.contains(KeyVerificationStart.VERIF_METHOD_SCAN)
|
val qrAvailable = pvr?.hasMethod(VerificationMethod.SCAN) ?: false
|
||||||
?: false
|
val emojiAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false
|
||||||
val emojiAvailable = pvr?.readyInfo?.methods?.contains(KeyVerificationStart.VERIF_METHOD_SAS)
|
|
||||||
?: false
|
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
QRModeAvailable = qrAvailable,
|
QRModeAvailable = qrAvailable,
|
||||||
SASMOdeAvailable = emojiAvailable
|
SASModeAvailable = emojiAvailable
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,15 +84,13 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
|
||||||
val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args()
|
val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args()
|
||||||
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
|
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
|
||||||
val pvr = session.getSasVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
|
val pvr = session.getSasVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
|
||||||
val qrAvailable = pvr?.readyInfo?.methods?.contains(KeyVerificationStart.VERIF_METHOD_SCAN)
|
val qrAvailable = pvr?.hasMethod(VerificationMethod.SCAN) ?: false
|
||||||
?: false
|
val emojiAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false
|
||||||
val emojiAvailable = pvr?.readyInfo?.methods?.contains(KeyVerificationStart.VERIF_METHOD_SAS)
|
|
||||||
?: false
|
|
||||||
|
|
||||||
return VerificationChooseMethodViewState(otherUserId = args.otherUserId,
|
return VerificationChooseMethodViewState(otherUserId = args.otherUserId,
|
||||||
transactionId = args.verificationId ?: "",
|
transactionId = args.verificationId ?: "",
|
||||||
QRModeAvailable = qrAvailable,
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.MvRxState
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
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.EmptyAction
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
|
||||||
data class SASVerificationConclusionViewState(
|
data class VerificationConclusionViewState(
|
||||||
val conclusionState: ConclusionState = ConclusionState.CANCELLED
|
val conclusionState: ConclusionState = ConclusionState.CANCELLED
|
||||||
) : MvRxState
|
) : MvRxState
|
||||||
|
|
||||||
|
@ -33,22 +33,22 @@ enum class ConclusionState {
|
||||||
CANCELLED
|
CANCELLED
|
||||||
}
|
}
|
||||||
|
|
||||||
class VerificationConclusionViewModel(initialState: SASVerificationConclusionViewState)
|
class VerificationConclusionViewModel(initialState: VerificationConclusionViewState)
|
||||||
: VectorViewModel<SASVerificationConclusionViewState, EmptyAction>(initialState) {
|
: 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>()
|
val args = viewModelContext.args<VerificationConclusionFragment.Args>()
|
||||||
|
|
||||||
return when (safeValueOf(args.cancelReason)) {
|
return when (safeValueOf(args.cancelReason)) {
|
||||||
CancelCode.MismatchedSas,
|
CancelCode.MismatchedSas,
|
||||||
CancelCode.MismatchedCommitment,
|
CancelCode.MismatchedCommitment,
|
||||||
CancelCode.MismatchedKeys -> {
|
CancelCode.MismatchedKeys -> {
|
||||||
SASVerificationConclusionViewState(ConclusionState.WARNING)
|
VerificationConclusionViewState(ConclusionState.WARNING)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
SASVerificationConclusionViewState(
|
VerificationConclusionViewState(
|
||||||
if (args.isSuccessFull) ConclusionState.SUCCESS
|
if (args.isSuccessFull) ConclusionState.SUCCESS
|
||||||
else ConclusionState.CANCELLED
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.airbnb.mvrx.*
|
||||||
import com.squareup.inject.assisted.Assisted
|
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.di.HasScreenInjector
|
||||||
import im.vector.riotx.core.platform.EmptyAction
|
import im.vector.riotx.core.platform.EmptyAction
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
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 transactionId: String?,
|
||||||
val otherUser: MatrixItem? = null,
|
val otherUser: MatrixItem? = null,
|
||||||
val supportsEmoji: Boolean = true,
|
val supportsEmoji: Boolean = true,
|
||||||
|
@ -38,10 +39,10 @@ data class SASVerificationCodeViewState(
|
||||||
val isWaitingFromOther: Boolean = false
|
val isWaitingFromOther: Boolean = false
|
||||||
) : MvRxState
|
) : MvRxState
|
||||||
|
|
||||||
class SASVerificationCodeViewModel @AssistedInject constructor(
|
class VerificationEmojiCodeViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: SASVerificationCodeViewState,
|
@Assisted initialState: VerificationEmojiCodeViewState,
|
||||||
private val session: Session
|
private val session: Session
|
||||||
) : VectorViewModel<SASVerificationCodeViewState, EmptyAction>(initialState), SasVerificationService.SasVerificationListener {
|
) : VectorViewModel<VerificationEmojiCodeViewState, EmptyAction>(initialState), SasVerificationService.SasVerificationListener {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
withState { state ->
|
withState { state ->
|
||||||
|
@ -141,22 +142,22 @@ class SASVerificationCodeViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
@AssistedInject.Factory
|
@AssistedInject.Factory
|
||||||
interface 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? {
|
override fun create(viewModelContext: ViewModelContext, state: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel? {
|
||||||
val factory = (viewModelContext as FragmentViewModelContext).fragment<SASVerificationCodeFragment>().viewModelFactory
|
val factory = (viewModelContext as FragmentViewModelContext).fragment<VerificationEmojiCodeFragment>().viewModelFactory
|
||||||
return factory.create(state)
|
return factory.create(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initialState(viewModelContext: ViewModelContext): SASVerificationCodeViewState? {
|
override fun initialState(viewModelContext: ViewModelContext): VerificationEmojiCodeViewState? {
|
||||||
val args = viewModelContext.args<VerificationBottomSheet.VerificationArgs>()
|
val args = viewModelContext.args<VerificationBottomSheet.VerificationArgs>()
|
||||||
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
|
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
|
||||||
val matrixItem = session.getUser(args.otherUserId)?.toMatrixItem()
|
val matrixItem = session.getUser(args.otherUserId)?.toMatrixItem()
|
||||||
|
|
||||||
return SASVerificationCodeViewState(
|
return VerificationEmojiCodeViewState(
|
||||||
transactionId = args.verificationId,
|
transactionId = args.verificationId,
|
||||||
otherUser = matrixItem
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.airbnb.mvrx.*
|
||||||
import com.squareup.inject.assisted.Assisted
|
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.di.HasScreenInjector
|
||||||
import im.vector.riotx.core.platform.EmptyAction
|
import im.vector.riotx.core.platform.EmptyAction
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
|
||||||
|
|
||||||
data class VerificationRequestViewState(
|
data class VerificationRequestViewState(
|
||||||
val roomId: String? = null,
|
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 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 DeclineVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction()
|
||||||
|
|
||||||
data class RequestVerification(val userId: 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 {
|
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 {
|
fun newIntent(context: Context, roomDetailArgs: RoomDetailArgs): Intent {
|
||||||
return Intent(context, RoomDetailActivity::class.java).apply {
|
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.Async
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Loading
|
import com.airbnb.mvrx.Loading
|
||||||
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
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.MessageImageInfoContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
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.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.model.message.MessageVideoContent
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
|
@ -849,6 +851,15 @@ class RoomDetailFragment @Inject constructor(
|
||||||
data.transactionId
|
data.transactionId
|
||||||
).show(parentFragmentManager, "REQ")
|
).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) {
|
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 {
|
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.core.utils.subscribeLogError
|
||||||
import im.vector.riotx.features.command.CommandParser
|
import im.vector.riotx.features.command.CommandParser
|
||||||
import im.vector.riotx.features.command.ParsedCommand
|
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.detail.timeline.helper.TimelineDisplayableEvents
|
||||||
import im.vector.riotx.features.home.room.typing.TypingHelper
|
import im.vector.riotx.features.home.room.typing.TypingHelper
|
||||||
import im.vector.riotx.features.settings.VectorPreferences
|
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.AcceptVerificationRequest -> handleAcceptVerification(action)
|
||||||
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
||||||
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
||||||
|
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,7 +410,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
is ParsedCommand.VerifyUser -> {
|
is ParsedCommand.VerifyUser -> {
|
||||||
session.getSasVerificationService().requestKeyVerificationInDMs(slashCommandResult.userId, room.roomId)
|
session.getSasVerificationService().requestKeyVerificationInDMs(supportedVerificationMethods, slashCommandResult.userId, room.roomId)
|
||||||
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
|
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
|
@ -824,6 +826,18 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
_requestLiveData.postValue(LiveEvent(Success(action)))
|
_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() {
|
private fun observeSyncState() {
|
||||||
session.rx()
|
session.rx()
|
||||||
.liveSyncState()
|
.liveSyncState()
|
||||||
|
|
|
@ -18,12 +18,8 @@ package im.vector.riotx.features.home.room.detail.readreceipts
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -57,11 +53,7 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list_with_title, container, false)
|
|
||||||
ButterKnife.bind(this, view)
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
|
|
@ -16,12 +16,8 @@
|
||||||
package im.vector.riotx.features.home.room.detail.timeline.action
|
package im.vector.riotx.features.home.room.detail.timeline.action
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -53,18 +49,12 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
||||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list, container, false)
|
|
||||||
ButterKnife.bind(this, view)
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
||||||
recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false)
|
recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false, disableItemAnimation = true)
|
||||||
// Disable item animation
|
|
||||||
recyclerView.itemAnimator = null
|
|
||||||
messageActionsEpoxyController.listener = this
|
messageActionsEpoxyController.listener = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,8 @@
|
||||||
package im.vector.riotx.features.home.room.detail.timeline.edithistory
|
package im.vector.riotx.features.home.room.detail.timeline.edithistory
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
|
@ -57,11 +53,7 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list_with_title, container, false)
|
|
||||||
ButterKnife.bind(this, view)
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
|
|
@ -17,12 +17,8 @@
|
||||||
package im.vector.riotx.features.home.room.detail.timeline.reactions
|
package im.vector.riotx.features.home.room.detail.timeline.reactions
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
|
@ -54,11 +50,7 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
|
||||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list_with_title, container, false)
|
|
||||||
ButterKnife.bind(this, view)
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
|
|
@ -18,12 +18,8 @@ package im.vector.riotx.features.home.room.list.actions
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -69,18 +65,12 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list
|
||||||
val view = inflater.inflate(R.layout.bottom_sheet_generic_list, container, false)
|
|
||||||
ButterKnife.bind(this, view)
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
||||||
recyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false)
|
recyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true)
|
||||||
// Disable item animation
|
|
||||||
recyclerView.itemAnimator = null
|
|
||||||
roomListActionsEpoxyController.listener = this
|
roomListActionsEpoxyController.listener = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,12 @@ import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import com.tapadoo.alerter.Alerter
|
import com.tapadoo.alerter.Alerter
|
||||||
import com.tapadoo.alerter.OnHideAlertListener
|
import com.tapadoo.alerter.OnHideAlertListener
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.features.crypto.verification.SASVerificationActivity
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
@ -78,8 +78,7 @@ object PopupAlertManager {
|
||||||
setLightStatusBar()
|
setLightStatusBar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (currentAlerter?.shouldBeDisplayedIn?.invoke(activity) == false) {
|
||||||
if (shouldIgnoreActivity(activity)) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,8 +107,6 @@ object PopupAlertManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldIgnoreActivity(activity: Activity) = activity is SASVerificationActivity
|
|
||||||
|
|
||||||
private fun displayNextIfPossible() {
|
private fun displayNextIfPossible() {
|
||||||
val currentActivity = weakCurrentActivity?.get()
|
val currentActivity = weakCurrentActivity?.get()
|
||||||
if (Alerter.isShowing || currentActivity == null) {
|
if (Alerter.isShowing || currentActivity == null) {
|
||||||
|
@ -209,7 +206,13 @@ object PopupAlertManager {
|
||||||
})
|
})
|
||||||
.enableSwipeToDismiss()
|
.enableSwipeToDismiss()
|
||||||
.enableInfiniteDuration(true)
|
.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()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +232,8 @@ object PopupAlertManager {
|
||||||
data class VectorAlert(val uid: String,
|
data class VectorAlert(val uid: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
val description: 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)
|
data class Button(val title: String, val action: Runnable, val autoClose: Boolean)
|
||||||
|
|
||||||
|
@ -250,5 +254,8 @@ object PopupAlertManager {
|
||||||
|
|
||||||
@ColorRes
|
@ColorRes
|
||||||
var colorRes: Int? = null
|
var colorRes: Int? = null
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
var colorInt: Int? = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
@ -32,7 +31,6 @@ import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
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? {
|
override fun getLayoutResId() = R.layout.bottom_sheet_logout_and_backup
|
||||||
val view = inflater.inflate(R.layout.bottom_sheet_logout_and_backup, container, false)
|
|
||||||
ButterKnife.bind(this, view)
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val dialog = super.onCreateDialog(savedInstanceState)
|
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/bottomSheetScrollView"
|
android:id="@+id/bottomSheetScrollView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -9,47 +10,62 @@
|
||||||
android:fadeScrollbars="false"
|
android:fadeScrollbars="false"
|
||||||
android:scrollbars="vertical">
|
android:scrollbars="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
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:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:layout_marginStart="8dp"
|
||||||
android:paddingStart="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:layout_weight="1"
|
||||||
android:paddingTop="16dp">
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
<ImageView
|
android:textColor="?riotx_text_primary"
|
||||||
android:id="@+id/verificationRequestAvatar"
|
android:textSize="20sp"
|
||||||
android:layout_width="32dp"
|
android:textStyle="bold"
|
||||||
android:layout_height="32dp"
|
app:layout_constraintBottom_toBottomOf="@+id/verificationRequestAvatar"
|
||||||
android:adjustViewBounds="true"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:background="@drawable/circle"
|
app:layout_constraintStart_toEndOf="@+id/verificationRequestAvatar"
|
||||||
android:contentDescription="@string/avatar"
|
app:layout_constraintTop_toTopOf="@+id/verificationRequestAvatar"
|
||||||
android:scaleType="centerCrop"
|
tools:text="@string/verification_verify_user" />
|
||||||
tools:src="@tools:sample/avatars" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/verificationRequestName"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/verification_request_alert_title"
|
|
||||||
android:textColor="?riotx_text_primary"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/bottomSheetFragmentContainer"
|
android:id="@+id/bottomSheetFragmentContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
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>
|
</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!-->
|
<!-- Sender name of a message when it is send by you, e.g. You: Hello!-->
|
||||||
<string name="you">You</string>
|
<string name="you">You</string>
|
||||||
|
|
||||||
<string name="verify_by_scanning_title">Verify by scanning</string>
|
<string name="verification_scan_notice">Scan the code with the other user\'s device to securely verify each other</string>
|
||||||
<!-- the %s will be replaced by verify_open_camera_link that will be clickable -->
|
<string name="verification_scan_their_code">Scan their code</string>
|
||||||
<string name="verify_by_scanning_description">Ask the other user to scan this code, or %s to scan theirs</string>
|
<string name="verification_scan_emoji_title">Can\'t scan</string>
|
||||||
<!-- This part is inserted in verify_by_scanning_description-->
|
<string name="verification_scan_emoji_subtitle">If you\'re not in person, compare emoji instead</string>
|
||||||
<string name="verify_open_camera_link">open your camera</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_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="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_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="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>
|
<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="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>
|
</resources>
|
||||||
|
|
Loading…
Add table
Reference in a new issue