Big annoying commit: execute command ./gradlew ktlintFormat
- Fix "chain-wrapping"
This commit is contained in:
parent
2ca3c68611
commit
a1caccbcc8
@ -67,9 +67,9 @@ class DeactivateAccountTest : InstrumentedTest {
|
|||||||
val throwable = commonTestHelper.logAccountWithError(session.myUserId, TestConstants.PASSWORD)
|
val throwable = commonTestHelper.logAccountWithError(session.myUserId, TestConstants.PASSWORD)
|
||||||
|
|
||||||
// Test the error
|
// Test the error
|
||||||
assertTrue(throwable is Failure.ServerError
|
assertTrue(throwable is Failure.ServerError &&
|
||||||
&& throwable.error.code == MatrixError.M_USER_DEACTIVATED
|
throwable.error.code == MatrixError.M_USER_DEACTIVATED &&
|
||||||
&& throwable.error.message == "This account has been deactivated")
|
throwable.error.message == "This account has been deactivated")
|
||||||
|
|
||||||
// Try to create an account with the deactivate account user id, it will fail (M_USER_IN_USE)
|
// Try to create an account with the deactivate account user id, it will fail (M_USER_IN_USE)
|
||||||
val hs = commonTestHelper.createHomeServerConfig()
|
val hs = commonTestHelper.createHomeServerConfig()
|
||||||
@ -95,8 +95,8 @@ class DeactivateAccountTest : InstrumentedTest {
|
|||||||
|
|
||||||
// Test the error
|
// Test the error
|
||||||
accountCreationError.let {
|
accountCreationError.let {
|
||||||
assertTrue(it is Failure.ServerError
|
assertTrue(it is Failure.ServerError &&
|
||||||
&& it.error.code == MatrixError.M_USER_IN_USE)
|
it.error.code == MatrixError.M_USER_IN_USE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to close the session, it has been deactivated
|
// No need to close the session, it has been deactivated
|
||||||
|
@ -50,8 +50,8 @@ class PreShareKeysTest : InstrumentedTest {
|
|||||||
aliceSession.cryptoService().discardOutboundSession(e2eRoomID)
|
aliceSession.cryptoService().discardOutboundSession(e2eRoomID)
|
||||||
|
|
||||||
val preShareCount = bobSession.cryptoService().getGossipingEvents().count {
|
val preShareCount = bobSession.cryptoService().getGossipingEvents().count {
|
||||||
it.senderId == aliceSession.myUserId
|
it.senderId == aliceSession.myUserId &&
|
||||||
&& it.getClearType() == EventType.ROOM_KEY
|
it.getClearType() == EventType.ROOM_KEY
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals("Bob should not have receive any key from alice at this point", 0, preShareCount)
|
assertEquals("Bob should not have receive any key from alice at this point", 0, preShareCount)
|
||||||
@ -65,16 +65,16 @@ class PreShareKeysTest : InstrumentedTest {
|
|||||||
mTestHelper.waitWithLatch { latch ->
|
mTestHelper.waitWithLatch { latch ->
|
||||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
val newGossipCount = bobSession.cryptoService().getGossipingEvents().count {
|
val newGossipCount = bobSession.cryptoService().getGossipingEvents().count {
|
||||||
it.senderId == aliceSession.myUserId
|
it.senderId == aliceSession.myUserId &&
|
||||||
&& it.getClearType() == EventType.ROOM_KEY
|
it.getClearType() == EventType.ROOM_KEY
|
||||||
}
|
}
|
||||||
newGossipCount > preShareCount
|
newGossipCount > preShareCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val latest = bobSession.cryptoService().getGossipingEvents().lastOrNull {
|
val latest = bobSession.cryptoService().getGossipingEvents().lastOrNull {
|
||||||
it.senderId == aliceSession.myUserId
|
it.senderId == aliceSession.myUserId &&
|
||||||
&& it.getClearType() == EventType.ROOM_KEY
|
it.getClearType() == EventType.ROOM_KEY
|
||||||
}
|
}
|
||||||
|
|
||||||
val content = latest?.getClearContent().toModel<RoomKeyContent>()
|
val content = latest?.getClearContent().toModel<RoomKeyContent>()
|
||||||
|
@ -91,8 +91,8 @@ internal class StateObserver(private val keysBackup: KeysBackupService,
|
|||||||
stateList.add(newState)
|
stateList.add(newState)
|
||||||
|
|
||||||
// Check that state transition is valid
|
// Check that state transition is valid
|
||||||
if (stateList.size >= 2
|
if (stateList.size >= 2 &&
|
||||||
&& !allowedStateTransitions.contains(stateList[stateList.size - 2] to newState)) {
|
!allowedStateTransitions.contains(stateList[stateList.size - 2] to newState)) {
|
||||||
// Forbidden transition detected
|
// Forbidden transition detected
|
||||||
lastTransitionError = "Forbidden transition detected from " + stateList[stateList.size - 2] + " to " + newState
|
lastTransitionError = "Forbidden transition detected from " + stateList[stateList.size - 2] + " to " + newState
|
||||||
}
|
}
|
||||||
|
@ -111,8 +111,8 @@ class TimelineBackToPreviousLastForwardTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ok, we have the 10 last messages from Alice.
|
// Ok, we have the 10 last messages from Alice.
|
||||||
snapshot.size == 10
|
snapshot.size == 10 &&
|
||||||
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(messageRoot).orFalse() }
|
snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(messageRoot).orFalse() }
|
||||||
}
|
}
|
||||||
|
|
||||||
bobTimeline.addListener(eventsListener)
|
bobTimeline.addListener(eventsListener)
|
||||||
@ -160,10 +160,10 @@ class TimelineBackToPreviousLastForwardTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bob can see the first event of the room (so Back pagination has worked)
|
// Bob can see the first event of the room (so Back pagination has worked)
|
||||||
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
|
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE &&
|
||||||
// 8 for room creation item, and 30 for the forward pagination
|
// 8 for room creation item, and 30 for the forward pagination
|
||||||
&& snapshot.size == 38
|
snapshot.size == 38 &&
|
||||||
&& snapshot.checkSendOrder(messageRoot, 30, 0)
|
snapshot.checkSendOrder(messageRoot, 30, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
bobTimeline.addListener(eventsListener)
|
bobTimeline.addListener(eventsListener)
|
||||||
|
@ -86,8 +86,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ok, we have the 10 last messages of the initial sync
|
// Ok, we have the 10 last messages of the initial sync
|
||||||
snapshot.size == 10
|
snapshot.size == 10 &&
|
||||||
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(message).orFalse() }
|
snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(message).orFalse() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the timeline at last sent message
|
// Open the timeline at last sent message
|
||||||
@ -110,8 +110,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The event is not in db, so it is fetch alone
|
// The event is not in db, so it is fetch alone
|
||||||
snapshot.size == 1
|
snapshot.size == 1 &&
|
||||||
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith("Message from Alice").orFalse() }
|
snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith("Message from Alice").orFalse() }
|
||||||
}
|
}
|
||||||
|
|
||||||
aliceTimeline.addListener(aliceEventsListener)
|
aliceTimeline.addListener(aliceEventsListener)
|
||||||
@ -137,9 +137,9 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alice can see the first event of the room (so Back pagination has worked)
|
// Alice can see the first event of the room (so Back pagination has worked)
|
||||||
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
|
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE &&
|
||||||
// 6 for room creation item (backward pagination), 1 for the context, and 50 for the forward pagination
|
// 6 for room creation item (backward pagination), 1 for the context, and 50 for the forward pagination
|
||||||
&& snapshot.size == 57 // 6 + 1 + 50
|
snapshot.size == 57 // 6 + 1 + 50
|
||||||
}
|
}
|
||||||
|
|
||||||
aliceTimeline.addListener(aliceEventsListener)
|
aliceTimeline.addListener(aliceEventsListener)
|
||||||
@ -166,8 +166,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
|||||||
Timber.w(" event ${it.root.content}")
|
Timber.w(" event ${it.root.content}")
|
||||||
}
|
}
|
||||||
// 6 for room creation item (backward pagination),and numberOfMessagesToSend (all the message of the room)
|
// 6 for room creation item (backward pagination),and numberOfMessagesToSend (all the message of the room)
|
||||||
snapshot.size == 6 + numberOfMessagesToSend
|
snapshot.size == 6 + numberOfMessagesToSend &&
|
||||||
&& snapshot.checkSendOrder(message, numberOfMessagesToSend, 0)
|
snapshot.checkSendOrder(message, numberOfMessagesToSend, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
aliceTimeline.addListener(aliceEventsListener)
|
aliceTimeline.addListener(aliceEventsListener)
|
||||||
|
@ -107,8 +107,8 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk
|
// Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk
|
||||||
snapshot.size == 10
|
snapshot.size == 10 &&
|
||||||
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(firstMessage).orFalse() }
|
snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(firstMessage).orFalse() }
|
||||||
}
|
}
|
||||||
|
|
||||||
bobTimeline.addListener(eventsListener)
|
bobTimeline.addListener(eventsListener)
|
||||||
@ -141,8 +141,8 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk
|
// Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk
|
||||||
snapshot.size == 10
|
snapshot.size == 10 &&
|
||||||
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(secondMessage).orFalse() }
|
snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(secondMessage).orFalse() }
|
||||||
}
|
}
|
||||||
|
|
||||||
bobTimeline.addListener(eventsListener)
|
bobTimeline.addListener(eventsListener)
|
||||||
@ -216,11 +216,11 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bob can see the first event of the room (so Back pagination has worked)
|
// Bob can see the first event of the room (so Back pagination has worked)
|
||||||
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
|
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE &&
|
||||||
// 8 for room creation item 60 message from Alice
|
// 8 for room creation item 60 message from Alice
|
||||||
&& snapshot.size == 68 // 8 + 60
|
snapshot.size == 68 && // 8 + 60
|
||||||
&& snapshot.checkSendOrder(secondMessage, 30, 0)
|
snapshot.checkSendOrder(secondMessage, 30, 0) &&
|
||||||
&& snapshot.checkSendOrder(firstMessage, 30, 30)
|
snapshot.checkSendOrder(firstMessage, 30, 30)
|
||||||
}
|
}
|
||||||
|
|
||||||
bobTimeline.addListener(eventsListener)
|
bobTimeline.addListener(eventsListener)
|
||||||
|
@ -90,8 +90,8 @@ internal class CurlLoggingInterceptor @Inject constructor()
|
|||||||
|
|
||||||
curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url.toString()
|
curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url.toString()
|
||||||
// Replace localhost for emulator by localhost for shell
|
// Replace localhost for emulator by localhost for shell
|
||||||
.replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/")
|
.replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/") +
|
||||||
+ "'")
|
"'")
|
||||||
|
|
||||||
// Add Json formatting
|
// Add Json formatting
|
||||||
curlCmd += " | python -m json.tool"
|
curlCmd += " | python -m json.tool"
|
||||||
|
@ -128,10 +128,10 @@ object MatrixPatterns {
|
|||||||
* @return true if the string is a valid event id.
|
* @return true if the string is a valid event id.
|
||||||
*/
|
*/
|
||||||
fun isEventId(str: String?): Boolean {
|
fun isEventId(str: String?): Boolean {
|
||||||
return str != null
|
return str != null &&
|
||||||
&& (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER
|
(str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER ||
|
||||||
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3
|
str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 ||
|
||||||
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
|
str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,24 +19,23 @@ package org.matrix.android.sdk.api.failure
|
|||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
import java.io.IOException
|
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
fun Throwable.is401() =
|
fun Throwable.is401() =
|
||||||
this is Failure.ServerError
|
this is Failure.ServerError &&
|
||||||
&& httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED && /* 401 */
|
||||||
&& error.code == MatrixError.M_UNAUTHORIZED
|
error.code == MatrixError.M_UNAUTHORIZED
|
||||||
|
|
||||||
fun Throwable.isTokenError() =
|
fun Throwable.isTokenError() =
|
||||||
this is Failure.ServerError
|
this is Failure.ServerError &&
|
||||||
&& (error.code == MatrixError.M_UNKNOWN_TOKEN
|
(error.code == MatrixError.M_UNKNOWN_TOKEN ||
|
||||||
|| error.code == MatrixError.M_MISSING_TOKEN
|
error.code == MatrixError.M_MISSING_TOKEN ||
|
||||||
|| error.code == MatrixError.ORG_MATRIX_EXPIRED_ACCOUNT)
|
error.code == MatrixError.ORG_MATRIX_EXPIRED_ACCOUNT)
|
||||||
|
|
||||||
fun Throwable.shouldBeRetried(): Boolean {
|
fun Throwable.shouldBeRetried(): Boolean {
|
||||||
return this is Failure.NetworkConnection
|
return this is Failure.NetworkConnection ||
|
||||||
|| this is IOException
|
this is IOException ||
|
||||||
|| (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED)
|
(this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,31 +51,31 @@ fun Throwable.getRetryDelay(defaultValue: Long): Long {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Throwable.isInvalidPassword(): Boolean {
|
fun Throwable.isInvalidPassword(): Boolean {
|
||||||
return this is Failure.ServerError
|
return this is Failure.ServerError &&
|
||||||
&& error.code == MatrixError.M_FORBIDDEN
|
error.code == MatrixError.M_FORBIDDEN &&
|
||||||
&& error.message == "Invalid password"
|
error.message == "Invalid password"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Throwable.isInvalidUIAAuth(): Boolean {
|
fun Throwable.isInvalidUIAAuth(): Boolean {
|
||||||
return this is Failure.ServerError
|
return this is Failure.ServerError &&
|
||||||
&& error.code == MatrixError.M_FORBIDDEN
|
error.code == MatrixError.M_FORBIDDEN &&
|
||||||
&& error.flows != null
|
error.flows != null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
||||||
*/
|
*/
|
||||||
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
||||||
return if (this is Failure.OtherServerError
|
return if (this is Failure.OtherServerError &&
|
||||||
&& httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */) {
|
httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */) {
|
||||||
tryOrNull {
|
tryOrNull {
|
||||||
MoshiProvider.providesMoshi()
|
MoshiProvider.providesMoshi()
|
||||||
.adapter(RegistrationFlowResponse::class.java)
|
.adapter(RegistrationFlowResponse::class.java)
|
||||||
.fromJson(errorBody)
|
.fromJson(errorBody)
|
||||||
}
|
}
|
||||||
} else if (this is Failure.ServerError
|
} else if (this is Failure.ServerError &&
|
||||||
&& httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED && /* 401 */
|
||||||
&& error.code == MatrixError.M_FORBIDDEN) {
|
error.code == MatrixError.M_FORBIDDEN) {
|
||||||
// This happens when the submission for this stage was bad (like bad password)
|
// This happens when the submission for this stage was bad (like bad password)
|
||||||
if (error.session != null && error.flows != null) {
|
if (error.session != null && error.flows != null) {
|
||||||
RegistrationFlowResponse(
|
RegistrationFlowResponse(
|
||||||
@ -94,9 +93,9 @@ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Throwable.isRegistrationAvailabilityError(): Boolean {
|
fun Throwable.isRegistrationAvailabilityError(): Boolean {
|
||||||
return this is Failure.ServerError
|
return this is Failure.ServerError &&
|
||||||
&& httpCode == HttpsURLConnection.HTTP_BAD_REQUEST /* 400 */
|
httpCode == HttpsURLConnection.HTTP_BAD_REQUEST && /* 400 */
|
||||||
&& (error.code == MatrixError.M_USER_IN_USE
|
(error.code == MatrixError.M_USER_IN_USE ||
|
||||||
|| error.code == MatrixError.M_INVALID_USERNAME
|
error.code == MatrixError.M_INVALID_USERNAME ||
|
||||||
|| error.code == MatrixError.M_EXCLUSIVE)
|
error.code == MatrixError.M_EXCLUSIVE)
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,8 @@ data class MXCrossSigningInfo(
|
|||||||
val crossSigningKeys: List<CryptoCrossSigningKey>
|
val crossSigningKeys: List<CryptoCrossSigningKey>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true
|
fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true &&
|
||||||
&& selfSigningKey()?.trustLevel?.isVerified() == true
|
selfSigningKey()?.trustLevel?.isVerified() == true
|
||||||
|
|
||||||
fun masterKey(): CryptoCrossSigningKey? = crossSigningKeys
|
fun masterKey(): CryptoCrossSigningKey? = crossSigningKeys
|
||||||
.firstOrNull { it.usages?.contains(KeyUsage.MASTER.value) == true }
|
.firstOrNull { it.usages?.contains(KeyUsage.MASTER.value) == true }
|
||||||
|
@ -48,8 +48,8 @@ data class PendingVerificationRequest(
|
|||||||
* SAS is supported if I support it and the other party support it
|
* SAS is supported if I support it and the other party support it
|
||||||
*/
|
*/
|
||||||
fun isSasSupported(): Boolean {
|
fun isSasSupported(): Boolean {
|
||||||
return requestInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse()
|
return requestInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() &&
|
||||||
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse()
|
readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,11 +57,11 @@ data class PendingVerificationRequest(
|
|||||||
*/
|
*/
|
||||||
fun otherCanShowQrCode(): Boolean {
|
fun otherCanShowQrCode(): Boolean {
|
||||||
return if (isIncoming) {
|
return if (isIncoming) {
|
||||||
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
|
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() &&
|
||||||
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
|
readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
|
||||||
} else {
|
} else {
|
||||||
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
|
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() &&
|
||||||
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
|
readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,11 +70,11 @@ data class PendingVerificationRequest(
|
|||||||
*/
|
*/
|
||||||
fun otherCanScanQrCode(): Boolean {
|
fun otherCanScanQrCode(): Boolean {
|
||||||
return if (isIncoming) {
|
return if (isIncoming) {
|
||||||
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
|
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() &&
|
||||||
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
|
readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
|
||||||
} else {
|
} else {
|
||||||
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
|
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() &&
|
||||||
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
|
readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,8 +238,8 @@ data class Event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isTextMessage(): Boolean {
|
fun Event.isTextMessage(): Boolean {
|
||||||
return getClearType() == EventType.MESSAGE
|
return getClearType() == EventType.MESSAGE &&
|
||||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||||
MessageType.MSGTYPE_TEXT,
|
MessageType.MSGTYPE_TEXT,
|
||||||
MessageType.MSGTYPE_EMOTE,
|
MessageType.MSGTYPE_EMOTE,
|
||||||
MessageType.MSGTYPE_NOTICE -> true
|
MessageType.MSGTYPE_NOTICE -> true
|
||||||
@ -248,40 +248,40 @@ fun Event.isTextMessage(): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isImageMessage(): Boolean {
|
fun Event.isImageMessage(): Boolean {
|
||||||
return getClearType() == EventType.MESSAGE
|
return getClearType() == EventType.MESSAGE &&
|
||||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||||
MessageType.MSGTYPE_IMAGE -> true
|
MessageType.MSGTYPE_IMAGE -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isVideoMessage(): Boolean {
|
fun Event.isVideoMessage(): Boolean {
|
||||||
return getClearType() == EventType.MESSAGE
|
return getClearType() == EventType.MESSAGE &&
|
||||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||||
MessageType.MSGTYPE_VIDEO -> true
|
MessageType.MSGTYPE_VIDEO -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isAudioMessage(): Boolean {
|
fun Event.isAudioMessage(): Boolean {
|
||||||
return getClearType() == EventType.MESSAGE
|
return getClearType() == EventType.MESSAGE &&
|
||||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||||
MessageType.MSGTYPE_AUDIO -> true
|
MessageType.MSGTYPE_AUDIO -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isFileMessage(): Boolean {
|
fun Event.isFileMessage(): Boolean {
|
||||||
return getClearType() == EventType.MESSAGE
|
return getClearType() == EventType.MESSAGE &&
|
||||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||||
MessageType.MSGTYPE_FILE -> true
|
MessageType.MSGTYPE_FILE -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isAttachmentMessage(): Boolean {
|
fun Event.isAttachmentMessage(): Boolean {
|
||||||
return getClearType() == EventType.MESSAGE
|
return getClearType() == EventType.MESSAGE &&
|
||||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||||
MessageType.MSGTYPE_IMAGE,
|
MessageType.MSGTYPE_IMAGE,
|
||||||
MessageType.MSGTYPE_AUDIO,
|
MessageType.MSGTYPE_AUDIO,
|
||||||
MessageType.MSGTYPE_VIDEO,
|
MessageType.MSGTYPE_VIDEO,
|
||||||
|
@ -106,13 +106,13 @@ object EventType {
|
|||||||
internal const val DUMMY = "m.dummy"
|
internal const val DUMMY = "m.dummy"
|
||||||
|
|
||||||
fun isCallEvent(type: String): Boolean {
|
fun isCallEvent(type: String): Boolean {
|
||||||
return type == CALL_INVITE
|
return type == CALL_INVITE ||
|
||||||
|| type == CALL_CANDIDATES
|
type == CALL_CANDIDATES ||
|
||||||
|| type == CALL_ANSWER
|
type == CALL_ANSWER ||
|
||||||
|| type == CALL_HANGUP
|
type == CALL_HANGUP ||
|
||||||
|| type == CALL_SELECT_ANSWER
|
type == CALL_SELECT_ANSWER ||
|
||||||
|| type == CALL_NEGOTIATE
|
type == CALL_NEGOTIATE ||
|
||||||
|| type == CALL_REJECT
|
type == CALL_REJECT ||
|
||||||
|| type == CALL_REPLACES
|
type == CALL_REPLACES
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,11 +50,11 @@ object MatrixLinkify {
|
|||||||
if (startPos == 0 || text[startPos - 1] != '/') {
|
if (startPos == 0 || text[startPos - 1] != '/') {
|
||||||
val endPos = match.range.last + 1
|
val endPos = match.range.last + 1
|
||||||
var url = text.substring(match.range)
|
var url = text.substring(match.range)
|
||||||
if (MatrixPatterns.isUserId(url)
|
if (MatrixPatterns.isUserId(url) ||
|
||||||
|| MatrixPatterns.isRoomAlias(url)
|
MatrixPatterns.isRoomAlias(url) ||
|
||||||
|| MatrixPatterns.isRoomId(url)
|
MatrixPatterns.isRoomId(url) ||
|
||||||
|| MatrixPatterns.isGroupId(url)
|
MatrixPatterns.isGroupId(url) ||
|
||||||
|| MatrixPatterns.isEventId(url)) {
|
MatrixPatterns.isEventId(url)) {
|
||||||
url = PermalinkService.MATRIX_TO_URL_BASE + url
|
url = PermalinkService.MATRIX_TO_URL_BASE + url
|
||||||
}
|
}
|
||||||
val span = MatrixPermalinkSpan(url, callback)
|
val span = MatrixPermalinkSpan(url, callback)
|
||||||
|
@ -172,8 +172,8 @@ internal class DefaultAuthenticationService @Inject constructor(
|
|||||||
return try {
|
return try {
|
||||||
getWellknownLoginFlowInternal(homeServerConnectionConfig)
|
getWellknownLoginFlowInternal(homeServerConnectionConfig)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
if (failure is Failure.OtherServerError
|
if (failure is Failure.OtherServerError &&
|
||||||
&& failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
|
failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
|
||||||
// 404, no well-known data, try direct access to the API
|
// 404, no well-known data, try direct access to the API
|
||||||
// First check the homeserver version
|
// First check the homeserver version
|
||||||
return runCatching {
|
return runCatching {
|
||||||
@ -190,8 +190,8 @@ internal class DefaultAuthenticationService @Inject constructor(
|
|||||||
it
|
it
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
if (it is Failure.OtherServerError
|
if (it is Failure.OtherServerError &&
|
||||||
&& it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
|
it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
|
||||||
// It's maybe a Web client url?
|
// It's maybe a Web client url?
|
||||||
getWebClientDomainLoginFlowInternal(homeServerConnectionConfig)
|
getWebClientDomainLoginFlowInternal(homeServerConnectionConfig)
|
||||||
} else {
|
} else {
|
||||||
@ -225,8 +225,8 @@ internal class DefaultAuthenticationService @Inject constructor(
|
|||||||
it
|
it
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
if (it is Failure.OtherServerError
|
if (it is Failure.OtherServerError &&
|
||||||
&& it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
|
it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
|
||||||
// Try with config.json
|
// Try with config.json
|
||||||
getWebClientLoginFlowInternal(homeServerConnectionConfig)
|
getWebClientLoginFlowInternal(homeServerConnectionConfig)
|
||||||
} else {
|
} else {
|
||||||
|
@ -54,8 +54,8 @@ internal class DefaultIsValidClientServerApiTask @Inject constructor(
|
|||||||
// We get a response, so the API is valid
|
// We get a response, so the API is valid
|
||||||
true
|
true
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
if (failure is Failure.OtherServerError
|
if (failure is Failure.OtherServerError &&
|
||||||
&& failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
|
failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
|
||||||
// Probably not valid
|
// Probably not valid
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,9 +63,9 @@ internal fun Versions.isSupportedBySdk(): Boolean {
|
|||||||
* Return true if the SDK supports this homeserver version for login and registration
|
* Return true if the SDK supports this homeserver version for login and registration
|
||||||
*/
|
*/
|
||||||
internal fun Versions.isLoginAndRegistrationSupportedBySdk(): Boolean {
|
internal fun Versions.isLoginAndRegistrationSupportedBySdk(): Boolean {
|
||||||
return !doesServerRequireIdentityServerParam()
|
return !doesServerRequireIdentityServerParam() &&
|
||||||
&& doesServerAcceptIdentityAccessToken()
|
doesServerAcceptIdentityAccessToken() &&
|
||||||
&& doesServerSeparatesAddAndBind()
|
doesServerSeparatesAddAndBind()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,8 +74,8 @@ internal fun Versions.isLoginAndRegistrationSupportedBySdk(): Boolean {
|
|||||||
* @return true if the server support the lazy loading of room members
|
* @return true if the server support the lazy loading of room members
|
||||||
*/
|
*/
|
||||||
private fun Versions.supportLazyLoadMembers(): Boolean {
|
private fun Versions.supportLazyLoadMembers(): Boolean {
|
||||||
return getMaxVersion() >= HomeServerVersion.r0_5_0
|
return getMaxVersion() >= HomeServerVersion.r0_5_0 ||
|
||||||
|| unstableFeatures?.get(FEATURE_LAZY_LOAD_MEMBERS) == true
|
unstableFeatures?.get(FEATURE_LAZY_LOAD_MEMBERS) == true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,13 +92,13 @@ private fun Versions.doesServerRequireIdentityServerParam(): Boolean {
|
|||||||
* Some homeservers may trigger errors if they are not prepared for the new parameter.
|
* Some homeservers may trigger errors if they are not prepared for the new parameter.
|
||||||
*/
|
*/
|
||||||
private fun Versions.doesServerAcceptIdentityAccessToken(): Boolean {
|
private fun Versions.doesServerAcceptIdentityAccessToken(): Boolean {
|
||||||
return getMaxVersion() >= HomeServerVersion.r0_6_0
|
return getMaxVersion() >= HomeServerVersion.r0_6_0 ||
|
||||||
|| unstableFeatures?.get(FEATURE_ID_ACCESS_TOKEN) ?: false
|
unstableFeatures?.get(FEATURE_ID_ACCESS_TOKEN) ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Versions.doesServerSeparatesAddAndBind(): Boolean {
|
private fun Versions.doesServerSeparatesAddAndBind(): Boolean {
|
||||||
return getMaxVersion() >= HomeServerVersion.r0_6_0
|
return getMaxVersion() >= HomeServerVersion.r0_6_0 ||
|
||||||
|| unstableFeatures?.get(FEATURE_SEPARATE_ADD_AND_BIND) ?: false
|
unstableFeatures?.get(FEATURE_SEPARATE_ADD_AND_BIND) ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Versions.getMaxVersion(): HomeServerVersion {
|
private fun Versions.getMaxVersion(): HomeServerVersion {
|
||||||
|
@ -868,8 +868,8 @@ internal class DefaultCryptoService @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getRoomUserIds(roomId: String): List<String> {
|
private fun getRoomUserIds(roomId: String): List<String> {
|
||||||
val encryptForInvitedMembers = isEncryptionEnabledForInvitedUser()
|
val encryptForInvitedMembers = isEncryptionEnabledForInvitedUser() &&
|
||||||
&& shouldEncryptForInvitedMembers(roomId)
|
shouldEncryptForInvitedMembers(roomId)
|
||||||
return cryptoSessionInfoProvider.getRoomUserIds(roomId, encryptForInvitedMembers)
|
return cryptoSessionInfoProvider.getRoomUserIds(roomId, encryptForInvitedMembers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -887,9 +887,9 @@ internal class DefaultCryptoService @Inject constructor(
|
|||||||
if (membership == Membership.JOIN) {
|
if (membership == Membership.JOIN) {
|
||||||
// make sure we are tracking the deviceList for this user.
|
// make sure we are tracking the deviceList for this user.
|
||||||
deviceListManager.startTrackingDeviceList(listOf(userId))
|
deviceListManager.startTrackingDeviceList(listOf(userId))
|
||||||
} else if (membership == Membership.INVITE
|
} else if (membership == Membership.INVITE &&
|
||||||
&& shouldEncryptForInvitedMembers(roomId)
|
shouldEncryptForInvitedMembers(roomId) &&
|
||||||
&& isEncryptionEnabledForInvitedUser()) {
|
isEncryptionEnabledForInvitedUser()) {
|
||||||
// track the deviceList for this invited user.
|
// track the deviceList for this invited user.
|
||||||
// Caution: there's a big edge case here in that federated servers do not
|
// Caution: there's a big edge case here in that federated servers do not
|
||||||
// know what other servers are in the room at the time they've been invited.
|
// know what other servers are in the room at the time they've been invited.
|
||||||
|
@ -475,8 +475,8 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isVerified) {
|
if (!isVerified) {
|
||||||
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":"
|
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" +
|
||||||
+ deviceKeys.deviceId + " with error " + errorMessage)
|
deviceKeys.deviceId + " with error " + errorMessage)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,9 +486,9 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||||||
// best off sticking with the original keys.
|
// best off sticking with the original keys.
|
||||||
//
|
//
|
||||||
// Should we warn the user about it somehow?
|
// Should we warn the user about it somehow?
|
||||||
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":"
|
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" +
|
||||||
+ deviceKeys.deviceId + " has changed : "
|
deviceKeys.deviceId + " has changed : " +
|
||||||
+ previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey)
|
previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey)
|
||||||
|
|
||||||
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
|
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
|
||||||
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
|
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
|
||||||
|
@ -107,8 +107,8 @@ internal class EventDecryptor @Inject constructor(
|
|||||||
} catch (mxCryptoError: MXCryptoError) {
|
} catch (mxCryptoError: MXCryptoError) {
|
||||||
Timber.v("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError")
|
Timber.v("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError")
|
||||||
if (algorithm == MXCRYPTO_ALGORITHM_OLM) {
|
if (algorithm == MXCRYPTO_ALGORITHM_OLM) {
|
||||||
if (mxCryptoError is MXCryptoError.Base
|
if (mxCryptoError is MXCryptoError.Base &&
|
||||||
&& mxCryptoError.errorType == MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE) {
|
mxCryptoError.errorType == MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE) {
|
||||||
// need to find sending device
|
// need to find sending device
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||||
val olmContent = event.content.toModel<OlmEventContent>()
|
val olmContent = event.content.toModel<OlmEventContent>()
|
||||||
|
@ -112,9 +112,8 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
|||||||
* @param andResend true to resend the key request
|
* @param andResend true to resend the key request
|
||||||
*/
|
*/
|
||||||
private fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody, andResend: Boolean) {
|
private fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody, andResend: Boolean) {
|
||||||
val req = cryptoStore.getOutgoingRoomKeyRequest(requestBody)
|
val req = cryptoStore.getOutgoingRoomKeyRequest(requestBody) // no request was made for this key
|
||||||
?: // no request was made for this key
|
?: return Unit.also {
|
||||||
return Unit.also {
|
|
||||||
Timber.v("## CRYPTO - GOSSIP cancelRoomKeyRequest() Unknown request $requestBody")
|
Timber.v("## CRYPTO - GOSSIP cancelRoomKeyRequest() Unknown request $requestBody")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +90,8 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
|||||||
oneTimeKey = key
|
oneTimeKey = key
|
||||||
}
|
}
|
||||||
if (oneTimeKey == null) {
|
if (oneTimeKey == null) {
|
||||||
Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
|
Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm +
|
||||||
+ " for device " + userId + " : " + deviceId)
|
" for device " + userId + " : " + deviceId)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Update the result for this device in results
|
// Update the result for this device in results
|
||||||
@ -126,15 +126,15 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
|||||||
sessionId = olmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value)
|
sessionId = olmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value)
|
||||||
|
|
||||||
if (!sessionId.isNullOrEmpty()) {
|
if (!sessionId.isNullOrEmpty()) {
|
||||||
Timber.v("## CRYPTO | verifyKeyAndStartSession() : Started new sessionid " + sessionId
|
Timber.v("## CRYPTO | verifyKeyAndStartSession() : Started new sessionid " + sessionId +
|
||||||
+ " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")")
|
" for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")")
|
||||||
} else {
|
} else {
|
||||||
// Possibly a bad key
|
// Possibly a bad key
|
||||||
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId")
|
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId
|
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId +
|
||||||
+ ":" + deviceId + " Error " + errorMessage)
|
":" + deviceId + " Error " + errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +38,9 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o
|
|||||||
|
|
||||||
devices.filter {
|
devices.filter {
|
||||||
// Don't bother setting up session to ourself
|
// Don't bother setting up session to ourself
|
||||||
it.identityKey() != olmDevice.deviceCurve25519Key
|
it.identityKey() != olmDevice.deviceCurve25519Key &&
|
||||||
// Don't bother setting up sessions with blocked users
|
// Don't bother setting up sessions with blocked users
|
||||||
&& !(it.trustLevel?.isVerified() ?: false)
|
!(it.trustLevel?.isVerified() ?: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||||
|
@ -82,9 +82,9 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||||||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
|
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
|
||||||
?: throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
?: throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||||
|
|
||||||
if (encryptedEventContent.senderKey.isNullOrBlank()
|
if (encryptedEventContent.senderKey.isNullOrBlank() ||
|
||||||
|| encryptedEventContent.sessionId.isNullOrBlank()
|
encryptedEventContent.sessionId.isNullOrBlank() ||
|
||||||
|| encryptedEventContent.ciphertext.isNullOrBlank()) {
|
encryptedEventContent.ciphertext.isNullOrBlank()) {
|
||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,11 +155,11 @@ internal class MXMegolmEncryption(
|
|||||||
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
|
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
|
||||||
Timber.v("## CRYPTO | ensureOutboundSession start")
|
Timber.v("## CRYPTO | ensureOutboundSession start")
|
||||||
var session = outboundSession
|
var session = outboundSession
|
||||||
if (session == null
|
if (session == null ||
|
||||||
// Need to make a brand new session?
|
// Need to make a brand new session?
|
||||||
|| session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
|
session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) ||
|
||||||
// Determine if we have shared with anyone we shouldn't have
|
// Determine if we have shared with anyone we shouldn't have
|
||||||
|| session.sharedWithTooManyDevices(devicesInRoom)) {
|
session.sharedWithTooManyDevices(devicesInRoom)) {
|
||||||
session = prepareNewSessionInRoom()
|
session = prepareNewSessionInRoom()
|
||||||
outboundSession = session
|
outboundSession = session
|
||||||
}
|
}
|
||||||
@ -380,8 +380,8 @@ internal class MXMegolmEncryption(
|
|||||||
// with them, which means that they will have announced any new devices via
|
// with them, which means that they will have announced any new devices via
|
||||||
// an m.new_device.
|
// an m.new_device.
|
||||||
val keys = deviceListManager.downloadKeys(userIds, false)
|
val keys = deviceListManager.downloadKeys(userIds, false)
|
||||||
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() ||
|
||||||
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
||||||
|
|
||||||
val devicesInRoom = DeviceInRoomInfo()
|
val devicesInRoom = DeviceInRoomInfo()
|
||||||
val unknownDevices = MXUsersDevicesMap<CryptoDeviceInfo>()
|
val unknownDevices = MXUsersDevicesMap<CryptoDeviceInfo>()
|
||||||
@ -446,10 +446,9 @@ internal class MXMegolmEncryption(
|
|||||||
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
||||||
val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||||
val olmSessionResult = usersDeviceMap.getObject(userId, deviceId)
|
val olmSessionResult = usersDeviceMap.getObject(userId, deviceId)
|
||||||
olmSessionResult?.sessionId
|
olmSessionResult?.sessionId // no session with this device, probably because there were no one-time keys.
|
||||||
?: // no session with this device, probably because there were no one-time keys.
|
|
||||||
// ensureOlmSessionsForDevicesAction has already done the logging, so just skip it.
|
// ensureOlmSessionsForDevicesAction has already done the logging, so just skip it.
|
||||||
return false.also {
|
?: return false.also {
|
||||||
Timber.w("## Crypto reshareKey: no session with this device, probably because there were no one-time keys")
|
Timber.w("## Crypto reshareKey: no session with this device, probably because there were no one-time keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,13 +529,13 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun canCrossSign(): Boolean {
|
override fun canCrossSign(): Boolean {
|
||||||
return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null
|
return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null &&
|
||||||
&& cryptoStore.getCrossSigningPrivateKeys()?.user != null
|
cryptoStore.getCrossSigningPrivateKeys()?.user != null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allPrivateKeysKnown(): Boolean {
|
override fun allPrivateKeysKnown(): Boolean {
|
||||||
return checkSelfTrust().isVerified()
|
return checkSelfTrust().isVerified() &&
|
||||||
&& cryptoStore.getCrossSigningPrivateKeys()?.allKnown().orFalse()
|
cryptoStore.getCrossSigningPrivateKeys()?.allKnown().orFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun trustUser(otherUserId: String, callback: MatrixCallback<Unit>) {
|
override fun trustUser(otherUserId: String, callback: MatrixCallback<Unit>) {
|
||||||
|
@ -860,8 +860,8 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
if (failure is Failure.ServerError
|
if (failure is Failure.ServerError &&
|
||||||
&& failure.error.code == MatrixError.M_NOT_FOUND) {
|
failure.error.code == MatrixError.M_NOT_FOUND) {
|
||||||
// Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup
|
// Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup
|
||||||
callback.onSuccess(null)
|
callback.onSuccess(null)
|
||||||
} else {
|
} else {
|
||||||
@ -883,8 +883,8 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
if (failure is Failure.ServerError
|
if (failure is Failure.ServerError &&
|
||||||
&& failure.error.code == MatrixError.M_NOT_FOUND) {
|
failure.error.code == MatrixError.M_NOT_FOUND) {
|
||||||
// Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup
|
// Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup
|
||||||
callback.onSuccess(null)
|
callback.onSuccess(null)
|
||||||
} else {
|
} else {
|
||||||
@ -1042,8 +1042,8 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authData.privateKeySalt.isNullOrBlank()
|
if (authData.privateKeySalt.isNullOrBlank() ||
|
||||||
|| authData.privateKeyIterations == null) {
|
authData.privateKeyIterations == null) {
|
||||||
Timber.w("recoveryKeyFromPassword: Salt and/or iterations not found in key backup auth data")
|
Timber.w("recoveryKeyFromPassword: Salt and/or iterations not found in key backup auth data")
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
@ -44,16 +44,16 @@ internal class KeysBackupStateManager(private val uiHandler: Handler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val isEnabled: Boolean
|
val isEnabled: Boolean
|
||||||
get() = state == KeysBackupState.ReadyToBackUp
|
get() = state == KeysBackupState.ReadyToBackUp ||
|
||||||
|| state == KeysBackupState.WillBackUp
|
state == KeysBackupState.WillBackUp ||
|
||||||
|| state == KeysBackupState.BackingUp
|
state == KeysBackupState.BackingUp
|
||||||
|
|
||||||
// True if unknown or bad state
|
// True if unknown or bad state
|
||||||
val isStucked: Boolean
|
val isStucked: Boolean
|
||||||
get() = state == KeysBackupState.Unknown
|
get() = state == KeysBackupState.Unknown ||
|
||||||
|| state == KeysBackupState.Disabled
|
state == KeysBackupState.Disabled ||
|
||||||
|| state == KeysBackupState.WrongBackUpVersion
|
state == KeysBackupState.WrongBackUpVersion ||
|
||||||
|| state == KeysBackupState.NotTrusted
|
state == KeysBackupState.NotTrusted
|
||||||
|
|
||||||
fun addListener(listener: KeysBackupStateListener) {
|
fun addListener(listener: KeysBackupStateListener) {
|
||||||
synchronized(listeners) {
|
synchronized(listeners) {
|
||||||
|
@ -359,8 +359,8 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
|||||||
val keyInfo = (keyInfoResult as? KeyInfoResult.Success)?.keyInfo
|
val keyInfo = (keyInfoResult as? KeyInfoResult.Success)?.keyInfo
|
||||||
?: return IntegrityResult.Error(SharedSecretStorageError.UnknownKey(keyId ?: ""))
|
?: return IntegrityResult.Error(SharedSecretStorageError.UnknownKey(keyId ?: ""))
|
||||||
|
|
||||||
if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2
|
if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2 &&
|
||||||
&& keyInfo.content.algorithm != SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
|
keyInfo.content.algorithm != SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
|
||||||
// Unsupported algorithm
|
// Unsupported algorithm
|
||||||
return IntegrityResult.Error(
|
return IntegrityResult.Error(
|
||||||
SharedSecretStorageError.UnsupportedAlgorithm(keyInfo.content.algorithm ?: "")
|
SharedSecretStorageError.UnsupportedAlgorithm(keyInfo.content.algorithm ?: "")
|
||||||
|
@ -152,8 +152,8 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
// Check credentials
|
// Check credentials
|
||||||
// The device id may not have been provided in credentials.
|
// The device id may not have been provided in credentials.
|
||||||
// Check it only if provided, else trust the stored one.
|
// Check it only if provided, else trust the stored one.
|
||||||
if (currentMetadata.userId != userId
|
if (currentMetadata.userId != userId ||
|
||||||
|| (deviceId != null && deviceId != currentMetadata.deviceId)) {
|
(deviceId != null && deviceId != currentMetadata.deviceId)) {
|
||||||
Timber.w("## open() : Credentials do not match, close this store and delete data")
|
Timber.w("## open() : Credentials do not match, close this store and delete data")
|
||||||
deleteAll = true
|
deleteAll = true
|
||||||
currentMetadata = null
|
currentMetadata = null
|
||||||
@ -178,9 +178,9 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
|
|
||||||
override fun hasData(): Boolean {
|
override fun hasData(): Boolean {
|
||||||
return doWithRealm(realmConfiguration) {
|
return doWithRealm(realmConfiguration) {
|
||||||
!it.isEmpty
|
!it.isEmpty &&
|
||||||
// Check if there is a MetaData object
|
// Check if there is a MetaData object
|
||||||
&& it.where<CryptoMetadataEntity>().count() > 0
|
it.where<CryptoMetadataEntity>().count() > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1025,10 +1025,10 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
}.mapNotNull {
|
}.mapNotNull {
|
||||||
it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
|
it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
|
||||||
}.firstOrNull {
|
}.firstOrNull {
|
||||||
it.requestBody?.algorithm == requestBody.algorithm
|
it.requestBody?.algorithm == requestBody.algorithm &&
|
||||||
&& it.requestBody?.roomId == requestBody.roomId
|
it.requestBody?.roomId == requestBody.roomId &&
|
||||||
&& it.requestBody?.senderKey == requestBody.senderKey
|
it.requestBody?.senderKey == requestBody.senderKey &&
|
||||||
&& it.requestBody?.sessionId == requestBody.sessionId
|
it.requestBody?.sessionId == requestBody.sessionId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1113,10 +1113,10 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
.mapNotNull {
|
.mapNotNull {
|
||||||
it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
|
it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
|
||||||
}.firstOrNull {
|
}.firstOrNull {
|
||||||
it.requestBody?.algorithm == requestBody.algorithm
|
it.requestBody?.algorithm == requestBody.algorithm &&
|
||||||
&& it.requestBody?.sessionId == requestBody.sessionId
|
it.requestBody?.sessionId == requestBody.sessionId &&
|
||||||
&& it.requestBody?.senderKey == requestBody.senderKey
|
it.requestBody?.senderKey == requestBody.senderKey &&
|
||||||
&& it.requestBody?.roomId == requestBody.roomId
|
it.requestBody?.roomId == requestBody.roomId
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
|
@ -46,8 +46,8 @@ internal class DefaultDeleteDeviceTask @Inject constructor(
|
|||||||
cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams(params.userAuthParam?.asMap()))
|
cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams(params.userAuthParam?.asMap()))
|
||||||
}
|
}
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
if (params.userInteractiveAuthInterceptor == null
|
if (params.userInteractiveAuthInterceptor == null ||
|
||||||
|| !handleUIA(
|
!handleUIA(
|
||||||
failure = throwable,
|
failure = throwable,
|
||||||
interceptor = params.userInteractiveAuthInterceptor,
|
interceptor = params.userInteractiveAuthInterceptor,
|
||||||
retryBlock = { authUpdate ->
|
retryBlock = { authUpdate ->
|
||||||
|
@ -125,8 +125,8 @@ internal class DefaultInitializeCrossSigningTask @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
uploadSigningKeysTask.execute(uploadSigningKeysParams)
|
uploadSigningKeysTask.execute(uploadSigningKeysParams)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
if (params.interactiveAuthInterceptor == null
|
if (params.interactiveAuthInterceptor == null ||
|
||||||
|| !handleUIA(
|
!handleUIA(
|
||||||
failure = failure,
|
failure = failure,
|
||||||
interceptor = params.interactiveAuthInterceptor,
|
interceptor = params.interactiveAuthInterceptor,
|
||||||
retryBlock = { authUpdate ->
|
retryBlock = { authUpdate ->
|
||||||
|
@ -114,8 +114,8 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
|
|||||||
// No common key sharing/hashing/hmac/SAS methods.
|
// No common key sharing/hashing/hmac/SAS methods.
|
||||||
// If a device is unable to complete the verification because the devices are unable to find a common key sharing,
|
// If a device is unable to complete the verification because the devices are unable to find a common key sharing,
|
||||||
// hashing, hmac, or SAS method, then it should send a m.key.verification.cancel message
|
// hashing, hmac, or SAS method, then it should send a m.key.verification.cancel message
|
||||||
if (listOf(agreedProtocol, agreedHash, agreedMac).any { it.isNullOrBlank() }
|
if (listOf(agreedProtocol, agreedHash, agreedMac).any { it.isNullOrBlank() } ||
|
||||||
|| agreedShortCode.isNullOrEmpty()) {
|
agreedShortCode.isNullOrEmpty()) {
|
||||||
// Failed to find agreement
|
// Failed to find agreement
|
||||||
Timber.e("## SAS Failed to find agreement ")
|
Timber.e("## SAS Failed to find agreement ")
|
||||||
cancel(CancelCode.UnknownMethod)
|
cancel(CancelCode.UnknownMethod)
|
||||||
@ -241,12 +241,12 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
|
|||||||
override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) {
|
override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) {
|
||||||
Timber.v("## SAS I: received mac for request id:$transactionId")
|
Timber.v("## SAS I: received mac for request id:$transactionId")
|
||||||
// Check for state?
|
// Check for state?
|
||||||
if (state != VerificationTxState.SendingKey
|
if (state != VerificationTxState.SendingKey &&
|
||||||
&& state != VerificationTxState.KeySent
|
state != VerificationTxState.KeySent &&
|
||||||
&& state != VerificationTxState.ShortCodeReady
|
state != VerificationTxState.ShortCodeReady &&
|
||||||
&& state != VerificationTxState.ShortCodeAccepted
|
state != VerificationTxState.ShortCodeAccepted &&
|
||||||
&& state != VerificationTxState.SendingMac
|
state != VerificationTxState.SendingMac &&
|
||||||
&& state != VerificationTxState.MacSent) {
|
state != VerificationTxState.MacSent) {
|
||||||
Timber.e("## SAS I: received key from invalid state $state")
|
Timber.e("## SAS I: received key from invalid state $state")
|
||||||
cancel(CancelCode.UnexpectedMessage)
|
cancel(CancelCode.UnexpectedMessage)
|
||||||
return
|
return
|
||||||
|
@ -144,10 +144,10 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Check that the agreement is correct
|
// Check that the agreement is correct
|
||||||
if (!KNOWN_AGREEMENT_PROTOCOLS.contains(accept.keyAgreementProtocol)
|
if (!KNOWN_AGREEMENT_PROTOCOLS.contains(accept.keyAgreementProtocol) ||
|
||||||
|| !KNOWN_HASHES.contains(accept.hash)
|
!KNOWN_HASHES.contains(accept.hash) ||
|
||||||
|| !KNOWN_MACS.contains(accept.messageAuthenticationCode)
|
!KNOWN_MACS.contains(accept.messageAuthenticationCode) ||
|
||||||
|| accept.shortAuthenticationStrings.intersect(KNOWN_SHORT_CODES).isEmpty()) {
|
accept.shortAuthenticationStrings.intersect(KNOWN_SHORT_CODES).isEmpty()) {
|
||||||
Timber.e("## SAS O: received invalid accept")
|
Timber.e("## SAS O: received invalid accept")
|
||||||
cancel(CancelCode.UnknownMethod)
|
cancel(CancelCode.UnknownMethod)
|
||||||
return
|
return
|
||||||
@ -233,12 +233,12 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
|||||||
override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) {
|
override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) {
|
||||||
Timber.v("## SAS O: onKeyVerificationMac id:$transactionId")
|
Timber.v("## SAS O: onKeyVerificationMac id:$transactionId")
|
||||||
// There is starting to be a huge amount of state / race here :/
|
// There is starting to be a huge amount of state / race here :/
|
||||||
if (state != VerificationTxState.OnKeyReceived
|
if (state != VerificationTxState.OnKeyReceived &&
|
||||||
&& state != VerificationTxState.ShortCodeReady
|
state != VerificationTxState.ShortCodeReady &&
|
||||||
&& state != VerificationTxState.ShortCodeAccepted
|
state != VerificationTxState.ShortCodeAccepted &&
|
||||||
&& state != VerificationTxState.KeySent
|
state != VerificationTxState.KeySent &&
|
||||||
&& state != VerificationTxState.SendingMac
|
state != VerificationTxState.SendingMac &&
|
||||||
&& state != VerificationTxState.MacSent) {
|
state != VerificationTxState.MacSent) {
|
||||||
Timber.e("## SAS O: received mac from invalid state $state")
|
Timber.e("## SAS O: received mac from invalid state $state")
|
||||||
cancel(CancelCode.UnexpectedMessage)
|
cancel(CancelCode.UnexpectedMessage)
|
||||||
return
|
return
|
||||||
|
@ -537,8 +537,8 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
// 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)
|
||||||
// I need to check if the pending request was related to this device also
|
// I need to check if the pending request was related to this device also
|
||||||
val autoAccept = getExistingVerificationRequests(otherUserId).any {
|
val autoAccept = getExistingVerificationRequests(otherUserId).any {
|
||||||
it.transactionId == startReq.transactionId
|
it.transactionId == startReq.transactionId &&
|
||||||
&& (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
(it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
||||||
}
|
}
|
||||||
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
||||||
// this,
|
// this,
|
||||||
@ -1278,8 +1278,8 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
private fun updatePendingRequest(updated: PendingVerificationRequest) {
|
private fun updatePendingRequest(updated: PendingVerificationRequest) {
|
||||||
val requestsForUser = pendingRequests.getOrPut(updated.otherUserId) { mutableListOf() }
|
val requestsForUser = pendingRequests.getOrPut(updated.otherUserId) { mutableListOf() }
|
||||||
val index = requestsForUser.indexOfFirst {
|
val index = requestsForUser.indexOfFirst {
|
||||||
it.transactionId == updated.transactionId
|
it.transactionId == updated.transactionId ||
|
||||||
|| it.transactionId == null && it.localId == updated.localId
|
it.transactionId == null && it.localId == updated.localId
|
||||||
}
|
}
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
requestsForUser.removeAt(index)
|
requestsForUser.removeAt(index)
|
||||||
|
@ -73,8 +73,8 @@ internal interface VerificationInfoStart : VerificationInfo<ValidVerificationInf
|
|||||||
val validHashes = hashes?.takeIf { it.contains("sha256") } ?: return null
|
val validHashes = hashes?.takeIf { it.contains("sha256") } ?: return null
|
||||||
val validMessageAuthenticationCodes = messageAuthenticationCodes
|
val validMessageAuthenticationCodes = messageAuthenticationCodes
|
||||||
?.takeIf {
|
?.takeIf {
|
||||||
it.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256)
|
it.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256) ||
|
||||||
|| it.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256_LONGKDF)
|
it.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256_LONGKDF)
|
||||||
}
|
}
|
||||||
?: return null
|
?: return null
|
||||||
val validShortAuthenticationStrings = shortAuthenticationStrings?.takeIf { it.contains(SasMode.DECIMAL) } ?: return null
|
val validShortAuthenticationStrings = shortAuthenticationStrings?.takeIf { it.contains(SasMode.DECIMAL) } ?: return null
|
||||||
|
@ -29,8 +29,8 @@ internal object IsUselessResolver {
|
|||||||
return when (event.type) {
|
return when (event.type) {
|
||||||
EventType.STATE_ROOM_MEMBER -> {
|
EventType.STATE_ROOM_MEMBER -> {
|
||||||
// Call toContent(), to filter out null value
|
// Call toContent(), to filter out null value
|
||||||
event.content != null
|
event.content != null &&
|
||||||
&& event.content.toContent() == event.resolvedPrevContent()?.toContent()
|
event.content.toContent() == event.resolvedPrevContent()?.toContent()
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
@ -75,9 +75,9 @@ class WellKnown {
|
|||||||
(config as? Map<*, *>)?.let { map ->
|
(config as? Map<*, *>)?.let { map ->
|
||||||
val apiUrl = map["api_url"] as? String
|
val apiUrl = map["api_url"] as? String
|
||||||
val uiUrl = map["ui_url"] as? String ?: apiUrl
|
val uiUrl = map["ui_url"] as? String ?: apiUrl
|
||||||
if (apiUrl != null
|
if (apiUrl != null &&
|
||||||
&& apiUrl.startsWith("https://")
|
apiUrl.startsWith("https://") &&
|
||||||
&& uiUrl!!.startsWith("https://")) {
|
uiUrl!!.startsWith("https://")) {
|
||||||
managers.add(WellKnownManagerConfig(
|
managers.add(WellKnownManagerConfig(
|
||||||
apiUrl = apiUrl,
|
apiUrl = apiUrl,
|
||||||
uiUrl = uiUrl
|
uiUrl = uiUrl
|
||||||
|
@ -74,10 +74,10 @@ internal suspend inline fun <DATA> executeRequest(globalErrorReceiver: GlobalErr
|
|||||||
|
|
||||||
currentRetryCount++
|
currentRetryCount++
|
||||||
|
|
||||||
if (exception is Failure.ServerError
|
if (exception is Failure.ServerError &&
|
||||||
&& exception.httpCode == 429
|
exception.httpCode == 429 &&
|
||||||
&& exception.error.code == MatrixError.M_LIMIT_EXCEEDED
|
exception.error.code == MatrixError.M_LIMIT_EXCEEDED &&
|
||||||
&& currentRetryCount < maxRetriesCount) {
|
currentRetryCount < maxRetriesCount) {
|
||||||
// 429, we can retry
|
// 429, we can retry
|
||||||
delay(exception.getRetryDelay(1_000))
|
delay(exception.getRetryDelay(1_000))
|
||||||
} else if (canRetry && currentRetryCount < maxRetriesCount && exception.shouldBeRetried()) {
|
} else if (canRetry && currentRetryCount < maxRetriesCount && exception.shouldBeRetried()) {
|
||||||
|
@ -73,8 +73,8 @@ internal class UserAgentHolder @Inject constructor(private val context: Context,
|
|||||||
|
|
||||||
// if there is no user agent or cannot parse it
|
// if there is no user agent or cannot parse it
|
||||||
if (null == systemUserAgent || systemUserAgent.lastIndexOf(")") == -1 || !systemUserAgent.contains("(")) {
|
if (null == systemUserAgent || systemUserAgent.lastIndexOf(")") == -1 || !systemUserAgent.contains("(")) {
|
||||||
userAgent = (appName + "/" + appVersion + " ( Flavour " + flavorDescription
|
userAgent = (appName + "/" + appVersion + " ( Flavour " + flavorDescription +
|
||||||
+ "; MatrixAndroidSdk2 " + BuildConfig.SDK_VERSION + ")")
|
"; MatrixAndroidSdk2 " + BuildConfig.SDK_VERSION + ")")
|
||||||
} else {
|
} else {
|
||||||
// update
|
// update
|
||||||
userAgent = appName + "/" + appVersion + " " +
|
userAgent = appName + "/" + appVersion + " " +
|
||||||
|
@ -45,9 +45,9 @@ internal class DefaultChangePasswordTask @Inject constructor(
|
|||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
val registrationFlowResponse = throwable.toRegistrationFlowResponse()
|
val registrationFlowResponse = throwable.toRegistrationFlowResponse()
|
||||||
|
|
||||||
if (registrationFlowResponse != null
|
if (registrationFlowResponse != null &&
|
||||||
/* Avoid infinite loop */
|
/* Avoid infinite loop */
|
||||||
&& changePasswordParams.auth?.session == null) {
|
changePasswordParams.auth?.session == null) {
|
||||||
// Retry with authentication
|
// Retry with authentication
|
||||||
executeRequest(globalErrorReceiver) {
|
executeRequest(globalErrorReceiver) {
|
||||||
accountAPI.changePassword(
|
accountAPI.changePassword(
|
||||||
|
@ -67,8 +67,8 @@ internal class FileUploader @Inject constructor(
|
|||||||
// Check size limit
|
// Check size limit
|
||||||
val maxUploadFileSize = homeServerCapabilitiesService.getHomeServerCapabilities().maxUploadFileSize
|
val maxUploadFileSize = homeServerCapabilitiesService.getHomeServerCapabilities().maxUploadFileSize
|
||||||
|
|
||||||
if (maxUploadFileSize != HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN
|
if (maxUploadFileSize != HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN &&
|
||||||
&& file.length() > maxUploadFileSize) {
|
file.length() > maxUploadFileSize) {
|
||||||
// Known limitation and file too big for the server, save the pain to upload it
|
// Known limitation and file too big for the server, save the pain to upload it
|
||||||
throw Failure.ServerError(
|
throw Failure.ServerError(
|
||||||
error = MatrixError(
|
error = MatrixError(
|
||||||
|
@ -157,10 +157,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||||||
params.attachment.size
|
params.attachment.size
|
||||||
)
|
)
|
||||||
|
|
||||||
if (attachment.type == ContentAttachmentData.Type.IMAGE
|
if (attachment.type == ContentAttachmentData.Type.IMAGE &&
|
||||||
// Do not compress gif
|
// Do not compress gif
|
||||||
&& attachment.mimeType != MimeTypes.Gif
|
attachment.mimeType != MimeTypes.Gif &&
|
||||||
&& params.compressBeforeSending) {
|
params.compressBeforeSending) {
|
||||||
notifyTracker(params) { contentUploadStateTracker.setCompressingImage(it) }
|
notifyTracker(params) { contentUploadStateTracker.setCompressingImage(it) }
|
||||||
|
|
||||||
fileToUpload = imageCompressor.compress(workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
|
fileToUpload = imageCompressor.compress(workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
|
||||||
@ -177,10 +177,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.also { filesToDelete.add(it) }
|
.also { filesToDelete.add(it) }
|
||||||
} else if (attachment.type == ContentAttachmentData.Type.VIDEO
|
} else if (attachment.type == ContentAttachmentData.Type.VIDEO &&
|
||||||
// Do not compress gif
|
// Do not compress gif
|
||||||
&& attachment.mimeType != MimeTypes.Gif
|
attachment.mimeType != MimeTypes.Gif &&
|
||||||
&& params.compressBeforeSending) {
|
params.compressBeforeSending) {
|
||||||
fileToUpload = videoCompressor.compress(workingFile, object : ProgressListener {
|
fileToUpload = videoCompressor.compress(workingFile, object : ProgressListener {
|
||||||
override fun onProgress(progress: Int, total: Int) {
|
override fun onProgress(progress: Int, total: Int) {
|
||||||
notifyTracker(params) { contentUploadStateTracker.setCompressingVideo(it, progress.toFloat()) }
|
notifyTracker(params) { contentUploadStateTracker.setCompressingVideo(it, progress.toFloat()) }
|
||||||
|
@ -33,9 +33,9 @@ internal class DefaultFilterRepository @Inject constructor(@SessionDatabase priv
|
|||||||
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||||
val filterEntity = FilterEntity.get(realm)
|
val filterEntity = FilterEntity.get(realm)
|
||||||
// Filter has changed, or no filter Id yet
|
// Filter has changed, or no filter Id yet
|
||||||
filterEntity == null
|
filterEntity == null ||
|
||||||
|| filterEntity.filterBodyJson != filter.toJSONString()
|
filterEntity.filterBodyJson != filter.toJSONString() ||
|
||||||
|| filterEntity.filterId.isBlank()
|
filterEntity.filterId.isBlank()
|
||||||
}.also { hasChanged ->
|
}.also { hasChanged ->
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
// Filter is new or has changed, store it and reset the filter Id.
|
// Filter is new or has changed, store it and reset the filter Id.
|
||||||
|
@ -50,10 +50,10 @@ data class EventFilter(
|
|||||||
@Json(name = "not_types") val notTypes: List<String>? = null
|
@Json(name = "not_types") val notTypes: List<String>? = null
|
||||||
) {
|
) {
|
||||||
fun hasData(): Boolean {
|
fun hasData(): Boolean {
|
||||||
return limit != null
|
return limit != null ||
|
||||||
|| senders != null
|
senders != null ||
|
||||||
|| notSenders != null
|
notSenders != null ||
|
||||||
|| types != null
|
types != null ||
|
||||||
|| notTypes != null
|
notTypes != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,14 +73,14 @@ data class RoomEventFilter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun hasData(): Boolean {
|
fun hasData(): Boolean {
|
||||||
return (limit != null
|
return (limit != null ||
|
||||||
|| notSenders != null
|
notSenders != null ||
|
||||||
|| notTypes != null
|
notTypes != null ||
|
||||||
|| senders != null
|
senders != null ||
|
||||||
|| types != null
|
types != null ||
|
||||||
|| rooms != null
|
rooms != null ||
|
||||||
|| notRooms != null
|
notRooms != null ||
|
||||||
|| containsUrl != null
|
containsUrl != null ||
|
||||||
|| lazyLoadMembers != null)
|
lazyLoadMembers != null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,12 +59,12 @@ data class RoomFilter(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
fun hasData(): Boolean {
|
fun hasData(): Boolean {
|
||||||
return (notRooms != null
|
return (notRooms != null ||
|
||||||
|| rooms != null
|
rooms != null ||
|
||||||
|| ephemeral != null
|
ephemeral != null ||
|
||||||
|| includeLeave != null
|
includeLeave != null ||
|
||||||
|| state != null
|
state != null ||
|
||||||
|| timeline != null
|
timeline != null ||
|
||||||
|| accountData != null)
|
accountData != null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,12 +320,12 @@ internal class DefaultIdentityService @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Throwable.isInvalidToken(): Boolean {
|
private fun Throwable.isInvalidToken(): Boolean {
|
||||||
return this is Failure.ServerError
|
return this is Failure.ServerError &&
|
||||||
&& httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Throwable.isTermsNotSigned(): Boolean {
|
private fun Throwable.isTermsNotSigned(): Boolean {
|
||||||
return this is Failure.ServerError
|
return this is Failure.ServerError &&
|
||||||
&& httpCode == HttpsURLConnection.HTTP_FORBIDDEN /* 403 */
|
httpCode == HttpsURLConnection.HTTP_FORBIDDEN && /* 403 */
|
||||||
&& error.code == MatrixError.M_TERMS_NOT_SIGNED
|
error.code == MatrixError.M_TERMS_NOT_SIGNED
|
||||||
}
|
}
|
||||||
|
@ -117,8 +117,8 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
|
|||||||
return withOlmUtility { olmUtility ->
|
return withOlmUtility { olmUtility ->
|
||||||
threePids.map { threePid ->
|
threePids.map { threePid ->
|
||||||
base64ToBase64Url(
|
base64ToBase64Url(
|
||||||
olmUtility.sha256(threePid.value.lowercase(Locale.ROOT)
|
olmUtility.sha256(threePid.value.lowercase(Locale.ROOT) +
|
||||||
+ " " + threePid.toMedium() + " " + pepper)
|
" " + threePid.toMedium() + " " + pepper)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,9 @@ internal class IntegrationManagerConfigExtractor @Inject constructor() {
|
|||||||
(config as? Map<*, *>)?.let { map ->
|
(config as? Map<*, *>)?.let { map ->
|
||||||
val apiUrl = map["api_url"] as? String
|
val apiUrl = map["api_url"] as? String
|
||||||
val uiUrl = map["ui_url"] as? String ?: apiUrl
|
val uiUrl = map["ui_url"] as? String ?: apiUrl
|
||||||
if (apiUrl != null
|
if (apiUrl != null &&
|
||||||
&& apiUrl.startsWith("https://")
|
apiUrl.startsWith("https://") &&
|
||||||
&& uiUrl!!.startsWith("https://")) {
|
uiUrl!!.startsWith("https://")) {
|
||||||
return WellknownIntegrationManagerConfigEntity(
|
return WellknownIntegrationManagerConfigEntity(
|
||||||
apiUrl = apiUrl,
|
apiUrl = apiUrl,
|
||||||
uiUrl = uiUrl
|
uiUrl = uiUrl
|
||||||
|
@ -33,9 +33,9 @@ internal class UrlsExtractor @Inject constructor() {
|
|||||||
return event.takeIf { it.root.getClearType() == EventType.MESSAGE }
|
return event.takeIf { it.root.getClearType() == EventType.MESSAGE }
|
||||||
?.getLastMessageContent()
|
?.getLastMessageContent()
|
||||||
?.takeIf {
|
?.takeIf {
|
||||||
it.msgType == MessageType.MSGTYPE_TEXT
|
it.msgType == MessageType.MSGTYPE_TEXT ||
|
||||||
|| it.msgType == MessageType.MSGTYPE_NOTICE
|
it.msgType == MessageType.MSGTYPE_NOTICE ||
|
||||||
|| it.msgType == MessageType.MSGTYPE_EMOTE
|
it.msgType == MessageType.MSGTYPE_EMOTE
|
||||||
}
|
}
|
||||||
?.let { messageContent ->
|
?.let { messageContent ->
|
||||||
if (event.isReply()) {
|
if (event.isReply()) {
|
||||||
|
@ -71,8 +71,8 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
|
|||||||
}
|
}
|
||||||
true
|
true
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
if (params.userInteractiveAuthInterceptor == null
|
if (params.userInteractiveAuthInterceptor == null ||
|
||||||
|| !handleUIA(
|
!handleUIA(
|
||||||
failure = throwable,
|
failure = throwable,
|
||||||
interceptor = params.userInteractiveAuthInterceptor,
|
interceptor = params.userInteractiveAuthInterceptor,
|
||||||
retryBlock = { authUpdate ->
|
retryBlock = { authUpdate ->
|
||||||
|
@ -131,8 +131,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||||||
EventType.ENCRYPTED -> {
|
EventType.ENCRYPTED -> {
|
||||||
// Relation type is in clear
|
// Relation type is in clear
|
||||||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
|
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
|
||||||
if (encryptedEventContent?.relatesTo?.type == RelationType.REPLACE
|
if (encryptedEventContent?.relatesTo?.type == RelationType.REPLACE ||
|
||||||
|| encryptedEventContent?.relatesTo?.type == RelationType.RESPONSE
|
encryptedEventContent?.relatesTo?.type == RelationType.RESPONSE
|
||||||
) {
|
) {
|
||||||
event.getClearContent().toModel<MessageContent>()?.let {
|
event.getClearContent().toModel<MessageContent>()?.let {
|
||||||
if (encryptedEventContent.relatesTo.type == RelationType.REPLACE) {
|
if (encryptedEventContent.relatesTo.type == RelationType.REPLACE) {
|
||||||
|
@ -77,9 +77,9 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
|||||||
buildHistoryVisibilityEvent(params),
|
buildHistoryVisibilityEvent(params),
|
||||||
buildAvatarEvent(params),
|
buildAvatarEvent(params),
|
||||||
buildGuestAccess(params)
|
buildGuestAccess(params)
|
||||||
)
|
) +
|
||||||
+ params.featurePreset?.setupInitialStates().orEmpty()
|
params.featurePreset?.setupInitialStates().orEmpty() +
|
||||||
+ buildCustomInitialStates(params)
|
buildCustomInitialStates(params)
|
||||||
)
|
)
|
||||||
.takeIf { it.isNotEmpty() }
|
.takeIf { it.isNotEmpty() }
|
||||||
|
|
||||||
@ -154,8 +154,8 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
|||||||
* Add the crypto algorithm to the room creation parameters.
|
* Add the crypto algorithm to the room creation parameters.
|
||||||
*/
|
*/
|
||||||
private suspend fun buildEncryptionWithAlgorithmEvent(params: CreateRoomParams): Event? {
|
private suspend fun buildEncryptionWithAlgorithmEvent(params: CreateRoomParams): Event? {
|
||||||
if (params.algorithm == null
|
if (params.algorithm == null &&
|
||||||
&& canEnableEncryption(params)) {
|
canEnableEncryption(params)) {
|
||||||
// Enable the encryption
|
// Enable the encryption
|
||||||
params.enableEncryption()
|
params.enableEncryption()
|
||||||
}
|
}
|
||||||
@ -173,13 +173,13 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun canEnableEncryption(params: CreateRoomParams): Boolean {
|
private suspend fun canEnableEncryption(params: CreateRoomParams): Boolean {
|
||||||
return params.enableEncryptionIfInvitedUsersSupportIt
|
return params.enableEncryptionIfInvitedUsersSupportIt &&
|
||||||
// Parity with web, enable if users have encryption ready devices
|
// Parity with web, enable if users have encryption ready devices
|
||||||
// for now remove checks on cross signing and 3pid invites
|
// for now remove checks on cross signing and 3pid invites
|
||||||
// && crossSigningService.isCrossSigningVerified()
|
// && crossSigningService.isCrossSigningVerified()
|
||||||
&& params.invite3pids.isEmpty()
|
params.invite3pids.isEmpty() &&
|
||||||
&& params.invitedUserIds.isNotEmpty()
|
params.invitedUserIds.isNotEmpty() &&
|
||||||
&& params.invitedUserIds.let { userIds ->
|
params.invitedUserIds.let { userIds ->
|
||||||
val keys = deviceListManager.downloadKeys(userIds, forceDownload = false)
|
val keys = deviceListManager.downloadKeys(userIds, forceDownload = false)
|
||||||
|
|
||||||
userIds.all { userId ->
|
userIds.all { userId ->
|
||||||
|
@ -81,13 +81,13 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
|||||||
}
|
}
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
if (throwable is Failure.ServerError) {
|
if (throwable is Failure.ServerError) {
|
||||||
if (throwable.httpCode == 403
|
if (throwable.httpCode == 403 &&
|
||||||
&& throwable.error.code == MatrixError.M_FORBIDDEN
|
throwable.error.code == MatrixError.M_FORBIDDEN &&
|
||||||
&& throwable.error.message.startsWith("Federation denied with")) {
|
throwable.error.message.startsWith("Federation denied with")) {
|
||||||
throw CreateRoomFailure.CreatedWithFederationFailure(throwable.error)
|
throw CreateRoomFailure.CreatedWithFederationFailure(throwable.error)
|
||||||
} else if (throwable.httpCode == 400
|
} else if (throwable.httpCode == 400 &&
|
||||||
&& throwable.error.code == MatrixError.M_UNKNOWN
|
throwable.error.code == MatrixError.M_UNKNOWN &&
|
||||||
&& throwable.error.message == "Invalid characters in room alias") {
|
throwable.error.message == "Invalid characters in room alias") {
|
||||||
throw CreateRoomFailure.AliasError(RoomAliasError.AliasInvalid)
|
throw CreateRoomFailure.AliasError(RoomAliasError.AliasInvalid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,8 +138,8 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
|||||||
* @return true if it is a direct chat
|
* @return true if it is a direct chat
|
||||||
*/
|
*/
|
||||||
private fun CreateRoomParams.isDirect(): Boolean {
|
private fun CreateRoomParams.isDirect(): Boolean {
|
||||||
return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT
|
return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT &&
|
||||||
&& isDirect == true
|
isDirect == true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,8 +82,8 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
|
|||||||
markers[READ_MARKER] = fullyReadEventId
|
markers[READ_MARKER] = fullyReadEventId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (readReceiptEventId != null
|
if (readReceiptEventId != null &&
|
||||||
&& !isEventRead(monarchy.realmConfiguration, userId, params.roomId, readReceiptEventId)) {
|
!isEventRead(monarchy.realmConfiguration, userId, params.roomId, readReceiptEventId)) {
|
||||||
if (LocalEcho.isLocalEchoId(readReceiptEventId)) {
|
if (LocalEcho.isLocalEchoId(readReceiptEventId)) {
|
||||||
Timber.w("Can't set read receipt for local event $readReceiptEventId")
|
Timber.w("Can't set read receipt for local event $readReceiptEventId")
|
||||||
} else {
|
} else {
|
||||||
|
@ -105,8 +105,8 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hard to filter from the app now we use PagedList...
|
// Hard to filter from the app now we use PagedList...
|
||||||
roomSummaryEntity.isHiddenFromUser = roomSummaryEntity.versioningState == VersioningState.UPGRADED_ROOM_JOINED
|
roomSummaryEntity.isHiddenFromUser = roomSummaryEntity.versioningState == VersioningState.UPGRADED_ROOM_JOINED ||
|
||||||
|| roomAccountDataDataSource.getAccountDataEvent(roomId, RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM) != null
|
roomAccountDataDataSource.getAccountDataEvent(roomId, RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM) != null
|
||||||
|
|
||||||
val lastNameEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_NAME, stateKey = "")?.root
|
val lastNameEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_NAME, stateKey = "")?.root
|
||||||
val lastTopicEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_TOPIC, stateKey = "")?.root
|
val lastTopicEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_TOPIC, stateKey = "")?.root
|
||||||
@ -132,9 +132,9 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
roomSummaryEntity.lastActivityTime = lastActivityFromEvent
|
roomSummaryEntity.lastActivityTime = lastActivityFromEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
|
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0 ||
|
||||||
// avoid this call if we are sure there are unread events
|
// avoid this call if we are sure there are unread events
|
||||||
|| !isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId)
|
!isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId)
|
||||||
|
|
||||||
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(realm, roomId)
|
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(realm, roomId)
|
||||||
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(realm, roomId)
|
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(realm, roomId)
|
||||||
|
@ -475,8 +475,8 @@ internal class DefaultTimeline(
|
|||||||
val currentChunk = getLiveChunk()
|
val currentChunk = getLiveChunk()
|
||||||
val token = if (direction == Timeline.Direction.BACKWARDS) currentChunk?.prevToken else currentChunk?.nextToken
|
val token = if (direction == Timeline.Direction.BACKWARDS) currentChunk?.prevToken else currentChunk?.nextToken
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
if (direction == Timeline.Direction.BACKWARDS
|
if (direction == Timeline.Direction.BACKWARDS ||
|
||||||
|| (direction == Timeline.Direction.FORWARDS && currentChunk?.hasBeenALastForwardChunk().orFalse())) {
|
(direction == Timeline.Direction.FORWARDS && currentChunk?.hasBeenALastForwardChunk().orFalse())) {
|
||||||
// We are in the case where event exists, but we do not know the token.
|
// We are in the case where event exists, but we do not know the token.
|
||||||
// Fetch (again) the last event to get a token
|
// Fetch (again) the last event to get a token
|
||||||
val lastKnownEventId = if (direction == Timeline.Direction.FORWARDS) {
|
val lastKnownEventId = if (direction == Timeline.Direction.FORWARDS) {
|
||||||
@ -583,8 +583,8 @@ internal class DefaultTimeline(
|
|||||||
val transactionId = timelineEvent.root.unsignedData?.transactionId
|
val transactionId = timelineEvent.root.unsignedData?.transactionId
|
||||||
uiEchoManager.onSyncedEvent(transactionId)
|
uiEchoManager.onSyncedEvent(transactionId)
|
||||||
|
|
||||||
if (timelineEvent.isEncrypted()
|
if (timelineEvent.isEncrypted() &&
|
||||||
&& timelineEvent.root.mxDecryptionResult == null) {
|
timelineEvent.root.mxDecryptionResult == null) {
|
||||||
timelineEvent.root.eventId?.also { eventDecryptor.requestDecryption(TimelineEventDecryptor.DecryptionRequest(timelineEvent.root, timelineID)) }
|
timelineEvent.root.eventId?.also { eventDecryptor.requestDecryption(TimelineEventDecryptor.DecryptionRequest(timelineEvent.root, timelineID)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,8 +238,8 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
|
|||||||
it.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = false)
|
it.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = false)
|
||||||
}
|
}
|
||||||
val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
|
val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
|
||||||
val shouldUpdateSummary = roomSummaryEntity.latestPreviewableEvent == null
|
val shouldUpdateSummary = roomSummaryEntity.latestPreviewableEvent == null ||
|
||||||
|| (chunksToDelete.isNotEmpty() && currentChunk.isLastForward && direction == PaginationDirection.FORWARDS)
|
(chunksToDelete.isNotEmpty() && currentChunk.isLastForward && direction == PaginationDirection.FORWARDS)
|
||||||
if (shouldUpdateSummary) {
|
if (shouldUpdateSummary) {
|
||||||
roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,9 @@ internal class DefaultSignOutTask @Inject constructor(
|
|||||||
}
|
}
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
// Maybe due to https://github.com/matrix-org/synapse/issues/5756
|
// Maybe due to https://github.com/matrix-org/synapse/issues/5756
|
||||||
if (throwable is Failure.ServerError
|
if (throwable is Failure.ServerError &&
|
||||||
&& throwable.httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
throwable.httpCode == HttpURLConnection.HTTP_UNAUTHORIZED && /* 401 */
|
||||||
&& throwable.error.code == MatrixError.M_UNKNOWN_TOKEN) {
|
throwable.error.code == MatrixError.M_UNKNOWN_TOKEN) {
|
||||||
// Also throwable.error.isSoftLogout should be true
|
// Also throwable.error.isSoftLogout should be true
|
||||||
// Ignore
|
// Ignore
|
||||||
Timber.w("Ignore error due to https://github.com/matrix-org/synapse/issues/5755")
|
Timber.w("Ignore error due to https://github.com/matrix-org/synapse/issues/5755")
|
||||||
|
@ -76,9 +76,9 @@ internal class DefaultPeekSpaceTask @Inject constructor(
|
|||||||
if (depth >= maxDepth) return emptyList()
|
if (depth >= maxDepth) return emptyList()
|
||||||
val childRoomsIds = stateEvents
|
val childRoomsIds = stateEvents
|
||||||
.filter {
|
.filter {
|
||||||
it.type == EventType.STATE_SPACE_CHILD && !it.stateKey.isNullOrEmpty()
|
it.type == EventType.STATE_SPACE_CHILD && !it.stateKey.isNullOrEmpty() &&
|
||||||
// Children where via is not present are ignored.
|
// Children where via is not present are ignored.
|
||||||
&& it.content?.toModel<SpaceChildContent>()?.via != null
|
it.content?.toModel<SpaceChildContent>()?.via != null
|
||||||
}
|
}
|
||||||
.map { it.stateKey to it.content?.toModel<SpaceChildContent>() }
|
.map { it.stateKey to it.content?.toModel<SpaceChildContent>() }
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
|
|||||||
// Decrypt event if necessary
|
// Decrypt event if necessary
|
||||||
Timber.i("## CRYPTO | To device event from ${event.senderId} of type:${event.type}")
|
Timber.i("## CRYPTO | To device event from ${event.senderId} of type:${event.type}")
|
||||||
decryptToDeviceEvent(event, null)
|
decryptToDeviceEvent(event, null)
|
||||||
if (event.getClearType() == EventType.MESSAGE
|
if (event.getClearType() == EventType.MESSAGE &&
|
||||||
&& event.getClearContent()?.toModel<MessageContent>()?.msgType == "m.bad.encrypted") {
|
event.getClearContent()?.toModel<MessageContent>()?.msgType == "m.bad.encrypted") {
|
||||||
Timber.e("## CRYPTO | handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}")
|
Timber.e("## CRYPTO | handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}")
|
||||||
} else {
|
} else {
|
||||||
verificationService.onToDeviceEvent(event)
|
verificationService.onToDeviceEvent(event)
|
||||||
|
@ -63,8 +63,8 @@ internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncSta
|
|||||||
override fun getStep(): Int {
|
override fun getStep(): Int {
|
||||||
ensureCache()
|
ensureCache()
|
||||||
val state = cache?.step ?: InitialSyncStatus.STEP_INIT
|
val state = cache?.step ?: InitialSyncStatus.STEP_INIT
|
||||||
return if (state >= InitialSyncStatus.STEP_DOWNLOADED
|
return if (state >= InitialSyncStatus.STEP_DOWNLOADED &&
|
||||||
&& System.currentTimeMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) {
|
System.currentTimeMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) {
|
||||||
Timber.d("INIT_SYNC downloaded file is outdated, download it again")
|
Timber.d("INIT_SYNC downloaded file is outdated, download it again")
|
||||||
// The downloaded file is outdated
|
// The downloaded file is outdated
|
||||||
setStep(InitialSyncStatus.STEP_INIT)
|
setStep(InitialSyncStatus.STEP_INIT)
|
||||||
|
@ -155,8 +155,8 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
|
|||||||
return extractWidgetSequence(widgetFactory)
|
return extractWidgetSequence(widgetFactory)
|
||||||
.filter {
|
.filter {
|
||||||
val widgetType = it.widgetContent.type ?: return@filter false
|
val widgetType = it.widgetContent.type ?: return@filter false
|
||||||
(widgetTypes == null || widgetTypes.contains(widgetType))
|
(widgetTypes == null || widgetTypes.contains(widgetType)) &&
|
||||||
&& (excludedTypes == null || !excludedTypes.contains(widgetType))
|
(excludedTypes == null || !excludedTypes.contains(widgetType))
|
||||||
}
|
}
|
||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
|
@ -235,8 +235,8 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
|||||||
|
|
||||||
private val qrStartForActivityResult = registerStartForActivityResult { activityResult ->
|
private val qrStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||||
if (activityResult.resultCode == Activity.RESULT_OK) {
|
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
toast("QrCode: " + QrCodeScannerActivity.getResultText(activityResult.data)
|
toast("QrCode: " + QrCodeScannerActivity.getResultText(activityResult.data) +
|
||||||
+ " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(activityResult.data))
|
" is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(activityResult.data))
|
||||||
|
|
||||||
// Also update the current QR Code (reverse operation)
|
// Also update the current QR Code (reverse operation)
|
||||||
// renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "")
|
// renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "")
|
||||||
|
@ -76,8 +76,8 @@ class AppStateHandler @Inject constructor(
|
|||||||
|
|
||||||
fun setCurrentSpace(spaceId: String?, session: Session? = null) {
|
fun setCurrentSpace(spaceId: String?, session: Session? = null) {
|
||||||
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
|
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.BySpace
|
if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.BySpace &&
|
||||||
&& spaceId == selectedSpaceDataSource.currentValue?.orNull()?.space()?.roomId) return
|
spaceId == selectedSpaceDataSource.currentValue?.orNull()?.space()?.roomId) return
|
||||||
val spaceSum = spaceId?.let { uSession.getRoomSummary(spaceId) }
|
val spaceSum = spaceId?.let { uSession.getRoomSummary(spaceId) }
|
||||||
selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.BySpace(spaceSum)))
|
selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.BySpace(spaceSum)))
|
||||||
if (spaceId != null) {
|
if (spaceId != null) {
|
||||||
@ -91,8 +91,8 @@ class AppStateHandler @Inject constructor(
|
|||||||
|
|
||||||
fun setCurrentGroup(groupId: String?, session: Session? = null) {
|
fun setCurrentGroup(groupId: String?, session: Session? = null) {
|
||||||
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
|
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.ByLegacyGroup
|
if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.ByLegacyGroup &&
|
||||||
&& groupId == selectedSpaceDataSource.currentValue?.orNull()?.group()?.groupId) return
|
groupId == selectedSpaceDataSource.currentValue?.orNull()?.group()?.groupId) return
|
||||||
val activeGroup = groupId?.let { uSession.getGroupSummary(groupId) }
|
val activeGroup = groupId?.let { uSession.getGroupSummary(groupId) }
|
||||||
selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.ByLegacyGroup(activeGroup)))
|
selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.ByLegacyGroup(activeGroup)))
|
||||||
if (groupId != null) {
|
if (groupId != null) {
|
||||||
|
@ -105,8 +105,8 @@ class VectorApplication :
|
|||||||
|
|
||||||
private val powerKeyReceiver = object : BroadcastReceiver() {
|
private val powerKeyReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent) {
|
override fun onReceive(context: Context?, intent: Intent) {
|
||||||
if (intent.action == Intent.ACTION_SCREEN_OFF
|
if (intent.action == Intent.ACTION_SCREEN_OFF &&
|
||||||
&& vectorPreferences.useFlagPinCode()) {
|
vectorPreferences.useFlagPinCode()) {
|
||||||
pinLocker.screenIsOff()
|
pinLocker.screenIsOff()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,15 +62,15 @@ fun Session.startSyncing(context: Context) {
|
|||||||
* Tell is the session has unsaved e2e keys in the backup
|
* Tell is the session has unsaved e2e keys in the backup
|
||||||
*/
|
*/
|
||||||
fun Session.hasUnsavedKeys(): Boolean {
|
fun Session.hasUnsavedKeys(): Boolean {
|
||||||
return cryptoService().inboundGroupSessionsCount(false) > 0
|
return cryptoService().inboundGroupSessionsCount(false) > 0 &&
|
||||||
&& cryptoService().keysBackupService().state != KeysBackupState.ReadyToBackUp
|
cryptoService().keysBackupService().state != KeysBackupState.ReadyToBackUp
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Session.cannotLogoutSafely(): Boolean {
|
fun Session.cannotLogoutSafely(): Boolean {
|
||||||
// has some encrypted chat
|
// has some encrypted chat
|
||||||
return hasUnsavedKeys()
|
return hasUnsavedKeys() ||
|
||||||
// has local cross signing keys
|
// has local cross signing keys
|
||||||
|| (cryptoService().crossSigningService().allPrivateKeysKnown()
|
(cryptoService().crossSigningService().allPrivateKeysKnown() &&
|
||||||
// That are not backed up
|
// That are not backed up
|
||||||
&& !sharedSecretStorageService.isRecoverySetup())
|
!sharedSecretStorageService.isRecoverySetup())
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,8 @@ import im.vector.app.features.themes.ThemeUtils
|
|||||||
* Set a text in the TextView, or set visibility to GONE if the text is null
|
* Set a text in the TextView, or set visibility to GONE if the text is null
|
||||||
*/
|
*/
|
||||||
fun TextView.setTextOrHide(newText: CharSequence?, hideWhenBlank: Boolean = true, vararg relatedViews: View = emptyArray()) {
|
fun TextView.setTextOrHide(newText: CharSequence?, hideWhenBlank: Boolean = true, vararg relatedViews: View = emptyArray()) {
|
||||||
if (newText == null
|
if (newText == null ||
|
||||||
|| (newText.isBlank() && hideWhenBlank)) {
|
(newText.isBlank() && hideWhenBlank)) {
|
||||||
isVisible = false
|
isVisible = false
|
||||||
relatedViews.forEach { it.isVisible = false }
|
relatedViews.forEach { it.isVisible = false }
|
||||||
} else {
|
} else {
|
||||||
|
@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
|||||||
|
|
||||||
fun TimelineEvent.canReact(): Boolean {
|
fun TimelineEvent.canReact(): Boolean {
|
||||||
// Only event of type EventType.MESSAGE or EventType.STICKER are supported for the moment
|
// Only event of type EventType.MESSAGE or EventType.STICKER are supported for the moment
|
||||||
return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER)
|
return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER) &&
|
||||||
&& root.sendState == SendState.SYNCED
|
root.sendState == SendState.SYNCED &&
|
||||||
&& !root.isRedacted()
|
!root.isRedacted()
|
||||||
}
|
}
|
||||||
|
@ -136,8 +136,8 @@ class KeysBackupBanner @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSetup(nbOfKeys: Int) {
|
private fun renderSetup(nbOfKeys: Int) {
|
||||||
if (nbOfKeys == 0
|
if (nbOfKeys == 0 ||
|
||||||
|| DefaultSharedPreferences.getInstance(context).getBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false)) {
|
DefaultSharedPreferences.getInstance(context).getBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false)) {
|
||||||
// Do not display the setup banner if there is no keys to backup, or if the user has already closed it
|
// Do not display the setup banner if there is no keys to backup, or if the user has already closed it
|
||||||
isVisible = false
|
isVisible = false
|
||||||
} else {
|
} else {
|
||||||
|
@ -59,8 +59,8 @@ class SnapOnScrollListener(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
if (behavior == Behavior.NOTIFY_ON_SCROLL_STATE_IDLE
|
if (behavior == Behavior.NOTIFY_ON_SCROLL_STATE_IDLE &&
|
||||||
&& newState == RecyclerView.SCROLL_STATE_IDLE) {
|
newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||||
maybeNotifySnapPositionChange(recyclerView)
|
maybeNotifySnapPositionChange(recyclerView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,8 @@ import im.vector.app.features.notifications.NotificationUtils
|
|||||||
*/
|
*/
|
||||||
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
|
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
|
||||||
// no issue before Android M, battery optimisations did not exist
|
// no issue before Android M, battery optimisations did not exist
|
||||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
|
||||||
|| context.getSystemService<PowerManager>()?.isIgnoringBatteryOptimizations(context.packageName) == true
|
context.getSystemService<PowerManager>()?.isIgnoringBatteryOptimizations(context.packageName) == true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isAirplaneModeOn(context: Context): Boolean {
|
fun isAirplaneModeOn(context: Context): Boolean {
|
||||||
|
@ -220,8 +220,8 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
|
|||||||
|
|
||||||
private fun startNextActivityAndFinish(ignoreClearCredentials: Boolean = false) {
|
private fun startNextActivityAndFinish(ignoreClearCredentials: Boolean = false) {
|
||||||
val intent = when {
|
val intent = when {
|
||||||
args.clearCredentials
|
args.clearCredentials &&
|
||||||
&& !ignoreClearCredentials
|
!ignoreClearCredentials
|
||||||
&& (!args.isUserLoggedOut || args.isAccountDeactivated) -> {
|
&& (!args.isUserLoggedOut || args.isAccountDeactivated) -> {
|
||||||
// User has explicitly asked to log out or deactivated his account
|
// User has explicitly asked to log out or deactivated his account
|
||||||
navigator.openLogin(this, null)
|
navigator.openLogin(this, null)
|
||||||
|
@ -27,9 +27,9 @@ private val listOfPreviewableMimeTypes = listOf(
|
|||||||
|
|
||||||
fun ContentAttachmentData.isPreviewable(): Boolean {
|
fun ContentAttachmentData.isPreviewable(): Boolean {
|
||||||
// Preview supports image and video
|
// Preview supports image and video
|
||||||
return (type == ContentAttachmentData.Type.IMAGE
|
return (type == ContentAttachmentData.Type.IMAGE &&
|
||||||
&& listOfPreviewableMimeTypes.contains(getSafeMimeType() ?: ""))
|
listOfPreviewableMimeTypes.contains(getSafeMimeType() ?: "")) ||
|
||||||
|| type == ContentAttachmentData.Type.VIDEO
|
type == ContentAttachmentData.Type.VIDEO
|
||||||
}
|
}
|
||||||
|
|
||||||
data class GroupedContentAttachmentData(
|
data class GroupedContentAttachmentData(
|
||||||
|
@ -24,7 +24,7 @@ import org.matrix.android.sdk.api.util.MimeTypes.isMimeTypeImage
|
|||||||
* All images are editable, expect Gif
|
* All images are editable, expect Gif
|
||||||
*/
|
*/
|
||||||
fun ContentAttachmentData.isEditable(): Boolean {
|
fun ContentAttachmentData.isEditable(): Boolean {
|
||||||
return type == ContentAttachmentData.Type.IMAGE
|
return type == ContentAttachmentData.Type.IMAGE &&
|
||||||
&& getSafeMimeType()?.isMimeTypeImage() == true
|
getSafeMimeType()?.isMimeTypeImage() == true &&
|
||||||
&& getSafeMimeType() != MimeTypes.Gif
|
getSafeMimeType() != MimeTypes.Gif
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ class CommandAutocompletePolicy @Inject constructor() : AutocompletePolicy {
|
|||||||
|
|
||||||
// Only if text which starts with '/' and without space
|
// Only if text which starts with '/' and without space
|
||||||
override fun shouldShowPopup(text: Spannable?, cursorPos: Int): Boolean {
|
override fun shouldShowPopup(text: Spannable?, cursorPos: Int): Boolean {
|
||||||
return enabled && text?.startsWith("/") == true
|
return enabled && text?.startsWith("/") == true &&
|
||||||
&& !text.contains(" ")
|
!text.contains(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldDismissPopup(text: Spannable?, cursorPos: Int): Boolean {
|
override fun shouldDismissPopup(text: Spannable?, cursorPos: Int): Boolean {
|
||||||
|
@ -104,8 +104,8 @@ class JitsiCallViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun handleSwitchTo(action: JitsiCallViewActions.SwitchTo) = withState { state ->
|
private fun handleSwitchTo(action: JitsiCallViewActions.SwitchTo) = withState { state ->
|
||||||
// Check if it is the same conf
|
// Check if it is the same conf
|
||||||
if (action.args.roomId != state.roomId
|
if (action.args.roomId != state.roomId ||
|
||||||
|| action.args.widgetId != state.widgetId) {
|
action.args.widgetId != state.widgetId) {
|
||||||
if (action.withConfirmation) {
|
if (action.withConfirmation) {
|
||||||
// Ask confirmation to switch, but wait a bit for the Activity to quit the PiP mode
|
// Ask confirmation to switch, but wait a bit for the Activity to quit the PiP mode
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
@ -87,10 +87,10 @@ import org.matrix.android.sdk.api.session.room.model.Membership
|
|||||||
|
|
||||||
fun render(roomDetailViewState: RoomDetailViewState) {
|
fun render(roomDetailViewState: RoomDetailViewState) {
|
||||||
val summary = roomDetailViewState.asyncRoomSummary()
|
val summary = roomDetailViewState.asyncRoomSummary()
|
||||||
val newState = if (summary?.membership != Membership.JOIN
|
val newState = if (summary?.membership != Membership.JOIN ||
|
||||||
|| roomDetailViewState.isWebRTCCallOptionAvailable()
|
roomDetailViewState.isWebRTCCallOptionAvailable() ||
|
||||||
|| !roomDetailViewState.isAllowedToManageWidgets
|
!roomDetailViewState.isAllowedToManageWidgets ||
|
||||||
|| roomDetailViewState.jitsiState.widgetId == null) {
|
roomDetailViewState.jitsiState.widgetId == null) {
|
||||||
State.Unmount
|
State.Unmount
|
||||||
} else if (roomDetailViewState.jitsiState.deleteWidgetInProgress) {
|
} else if (roomDetailViewState.jitsiState.deleteWidgetInProgress) {
|
||||||
State.Progress
|
State.Progress
|
||||||
|
@ -636,8 +636,8 @@ class WebRtcCall(
|
|||||||
// We consider a call to be on hold only if *all* the tracks are on hold
|
// We consider a call to be on hold only if *all* the tracks are on hold
|
||||||
// (is this the right thing to do?)
|
// (is this the right thing to do?)
|
||||||
for (transceiver in peerConnection?.transceivers ?: emptyList()) {
|
for (transceiver in peerConnection?.transceivers ?: emptyList()) {
|
||||||
val trackOnHold = transceiver.currentDirection == RtpTransceiver.RtpTransceiverDirection.INACTIVE
|
val trackOnHold = transceiver.currentDirection == RtpTransceiver.RtpTransceiverDirection.INACTIVE ||
|
||||||
|| transceiver.currentDirection == RtpTransceiver.RtpTransceiverDirection.RECV_ONLY
|
transceiver.currentDirection == RtpTransceiver.RtpTransceiverDirection.RECV_ONLY
|
||||||
if (!trackOnHold) callOnHold = false
|
if (!trackOnHold) callOnHold = false
|
||||||
}
|
}
|
||||||
return callOnHold
|
return callOnHold
|
||||||
@ -891,8 +891,8 @@ class WebRtcCall(
|
|||||||
val polite = !mxCall.isOutgoing
|
val polite = !mxCall.isOutgoing
|
||||||
// Here we follow the perfect negotiation logic from
|
// Here we follow the perfect negotiation logic from
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation
|
// https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation
|
||||||
val offerCollision = description.type == SdpType.OFFER
|
val offerCollision = description.type == SdpType.OFFER &&
|
||||||
&& (makingOffer || peerConnection.signalingState() != PeerConnection.SignalingState.STABLE)
|
(makingOffer || peerConnection.signalingState() != PeerConnection.SignalingState.STABLE)
|
||||||
|
|
||||||
ignoreOffer = !polite && offerCollision
|
ignoreOffer = !polite && offerCollision
|
||||||
if (ignoreOffer) {
|
if (ignoreOffer) {
|
||||||
|
@ -104,8 +104,8 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
|
|||||||
* @return the local status value
|
* @return the local status value
|
||||||
*/
|
*/
|
||||||
fun getHash(): String {
|
fun getHash(): String {
|
||||||
return (VectorLocale.applicationLocale.toString()
|
return (VectorLocale.applicationLocale.toString() +
|
||||||
+ "_" + FontScale.getFontScaleValue(context).preferenceValue
|
"_" + FontScale.getFontScaleValue(context).preferenceValue +
|
||||||
+ "_" + ThemeUtils.getApplicationTheme(context))
|
"_" + ThemeUtils.getApplicationTheme(context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,8 +156,8 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted
|
|||||||
val filteredMappedContacts = mappedContacts
|
val filteredMappedContacts = mappedContacts
|
||||||
.filter { it.displayName.contains(state.searchTerm, true) }
|
.filter { it.displayName.contains(state.searchTerm, true) }
|
||||||
.filter { contactModel ->
|
.filter { contactModel ->
|
||||||
!state.onlyBoundContacts
|
!state.onlyBoundContacts ||
|
||||||
|| contactModel.emails.any { it.matrixId != null } || contactModel.msisdns.any { it.matrixId != null }
|
contactModel.emails.any { it.matrixId != null } || contactModel.msisdns.any { it.matrixId != null }
|
||||||
}
|
}
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
|
@ -88,26 +88,26 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
|
|||||||
override fun onStepProgress(step: StepProgressListener.Step) {
|
override fun onStepProgress(step: StepProgressListener.Step) {
|
||||||
when (step) {
|
when (step) {
|
||||||
is StepProgressListener.Step.ComputingKey -> {
|
is StepProgressListener.Step.ComputingKey -> {
|
||||||
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message)
|
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) +
|
||||||
+ "\n" + stringProvider.getString(R.string.keys_backup_restoring_computing_key_waiting_message),
|
"\n" + stringProvider.getString(R.string.keys_backup_restoring_computing_key_waiting_message),
|
||||||
step.progress,
|
step.progress,
|
||||||
step.total))
|
step.total))
|
||||||
}
|
}
|
||||||
is StepProgressListener.Step.DownloadingKey -> {
|
is StepProgressListener.Step.DownloadingKey -> {
|
||||||
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message)
|
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) +
|
||||||
+ "\n" + stringProvider.getString(R.string.keys_backup_restoring_downloading_backup_waiting_message),
|
"\n" + stringProvider.getString(R.string.keys_backup_restoring_downloading_backup_waiting_message),
|
||||||
isIndeterminate = true))
|
isIndeterminate = true))
|
||||||
}
|
}
|
||||||
is StepProgressListener.Step.ImportingKey -> {
|
is StepProgressListener.Step.ImportingKey -> {
|
||||||
Timber.d("backupKeys.ImportingKey.progress: ${step.progress}")
|
Timber.d("backupKeys.ImportingKey.progress: ${step.progress}")
|
||||||
// Progress 0 can take a while, display an indeterminate progress in this case
|
// Progress 0 can take a while, display an indeterminate progress in this case
|
||||||
if (step.progress == 0) {
|
if (step.progress == 0) {
|
||||||
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message)
|
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) +
|
||||||
+ "\n" + stringProvider.getString(R.string.keys_backup_restoring_importing_keys_waiting_message),
|
"\n" + stringProvider.getString(R.string.keys_backup_restoring_importing_keys_waiting_message),
|
||||||
isIndeterminate = true))
|
isIndeterminate = true))
|
||||||
} else {
|
} else {
|
||||||
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message)
|
loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) +
|
||||||
+ "\n" + stringProvider.getString(R.string.keys_backup_restoring_importing_keys_waiting_message),
|
"\n" + stringProvider.getString(R.string.keys_backup_restoring_importing_keys_waiting_message),
|
||||||
step.progress,
|
step.progress,
|
||||||
step.total))
|
step.total))
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
|
|||||||
fun canExit(): Boolean {
|
fun canExit(): Boolean {
|
||||||
val currentBackupState = keysBackupService.state
|
val currentBackupState = keysBackupService.state
|
||||||
|
|
||||||
return currentBackupState == KeysBackupState.Unknown
|
return currentBackupState == KeysBackupState.Unknown ||
|
||||||
|| currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver
|
currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,9 +226,9 @@ class KeyRequestHandler @Inject constructor(
|
|||||||
|
|
||||||
val alertMgrUniqueKey = alertManagerId(userId, deviceId)
|
val alertMgrUniqueKey = alertManagerId(userId, deviceId)
|
||||||
alertsToRequests[alertMgrUniqueKey]?.removeAll {
|
alertsToRequests[alertMgrUniqueKey]?.removeAll {
|
||||||
it.deviceId == request.deviceId
|
it.deviceId == request.deviceId &&
|
||||||
&& it.userId == request.userId
|
it.userId == request.userId &&
|
||||||
&& it.requestId == request.requestId
|
it.requestId == request.requestId
|
||||||
}
|
}
|
||||||
if (alertsToRequests[alertMgrUniqueKey]?.isEmpty() == true) {
|
if (alertsToRequests[alertMgrUniqueKey]?.isEmpty() == true) {
|
||||||
popupAlertManager.cancelAlert(alertMgrUniqueKey)
|
popupAlertManager.cancelAlert(alertMgrUniqueKey)
|
||||||
|
@ -81,9 +81,9 @@ class BootstrapCrossSigningTask @Inject constructor(
|
|||||||
Timber.d("## BootstrapCrossSigningTask: mode:${params.setupMode} Starting...")
|
Timber.d("## BootstrapCrossSigningTask: mode:${params.setupMode} Starting...")
|
||||||
// Ensure cross-signing is initialized. Due to migration it is maybe not always correctly initialized
|
// Ensure cross-signing is initialized. Due to migration it is maybe not always correctly initialized
|
||||||
|
|
||||||
val shouldSetCrossSigning = !crossSigningService.isCrossSigningInitialized()
|
val shouldSetCrossSigning = !crossSigningService.isCrossSigningInitialized() ||
|
||||||
|| (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !crossSigningService.allPrivateKeysKnown())
|
(params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !crossSigningService.allPrivateKeysKnown()) ||
|
||||||
|| (params.setupMode == SetupMode.HARD_RESET)
|
(params.setupMode == SetupMode.HARD_RESET)
|
||||||
if (shouldSetCrossSigning) {
|
if (shouldSetCrossSigning) {
|
||||||
Timber.d("## BootstrapCrossSigningTask: Cross signing not enabled, so initialize")
|
Timber.d("## BootstrapCrossSigningTask: Cross signing not enabled, so initialize")
|
||||||
params.progressListener?.onProgress(
|
params.progressListener?.onProgress(
|
||||||
@ -227,9 +227,9 @@ class BootstrapCrossSigningTask @Inject constructor(
|
|||||||
|
|
||||||
val knownMegolmSecret = session.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo()
|
val knownMegolmSecret = session.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo()
|
||||||
val isMegolmBackupSecretKnown = knownMegolmSecret != null && knownMegolmSecret.version == serverVersion?.version
|
val isMegolmBackupSecretKnown = knownMegolmSecret != null && knownMegolmSecret.version == serverVersion?.version
|
||||||
val shouldCreateKeyBackup = serverVersion == null
|
val shouldCreateKeyBackup = serverVersion == null ||
|
||||||
|| (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown)
|
(params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown) ||
|
||||||
|| (params.setupMode == SetupMode.HARD_RESET)
|
(params.setupMode == SetupMode.HARD_RESET)
|
||||||
if (shouldCreateKeyBackup) {
|
if (shouldCreateKeyBackup) {
|
||||||
// clear all existing backups
|
// clear all existing backups
|
||||||
while (serverVersion != null) {
|
while (serverVersion != null) {
|
||||||
|
@ -437,9 +437,9 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BootstrapResult.Failure -> {
|
is BootstrapResult.Failure -> {
|
||||||
if (bootstrapResult is BootstrapResult.GenericError
|
if (bootstrapResult is BootstrapResult.GenericError &&
|
||||||
&& bootstrapResult.failure is Failure.OtherServerError
|
bootstrapResult.failure is Failure.OtherServerError &&
|
||||||
&& bootstrapResult.failure.httpCode == 401) {
|
bootstrapResult.failure.httpCode == 401) {
|
||||||
// Ignore this error
|
// Ignore this error
|
||||||
} else {
|
} else {
|
||||||
_viewEvents.post(BootstrapViewEvents.ModalError(bootstrapResult.error ?: stringProvider.getString(R.string.matrix_error)))
|
_viewEvents.post(BootstrapViewEvents.ModalError(bootstrapResult.error ?: stringProvider.getString(R.string.matrix_error)))
|
||||||
|
@ -159,9 +159,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
|
|||||||
state.otherUserMxItem?.let { matrixItem ->
|
state.otherUserMxItem?.let { matrixItem ->
|
||||||
if (state.isMe) {
|
if (state.isMe) {
|
||||||
avatarRenderer.render(matrixItem, views.otherUserAvatarImageView)
|
avatarRenderer.render(matrixItem, views.otherUserAvatarImageView)
|
||||||
if (state.sasTransactionState == VerificationTxState.Verified
|
if (state.sasTransactionState == VerificationTxState.Verified ||
|
||||||
|| state.qrTransactionState == VerificationTxState.Verified
|
state.qrTransactionState == VerificationTxState.Verified ||
|
||||||
|| state.verifiedFromPrivateKeys) {
|
state.verifiedFromPrivateKeys) {
|
||||||
views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
|
views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
|
||||||
} else {
|
} else {
|
||||||
views.otherUserShield.render(RoomEncryptionTrustLevel.Warning)
|
views.otherUserShield.render(RoomEncryptionTrustLevel.Warning)
|
||||||
|
@ -172,9 +172,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if the verification is already done you can't cancel anymore
|
// if the verification is already done you can't cancel anymore
|
||||||
if (state.pendingRequest.invoke()?.cancelConclusion != null
|
if (state.pendingRequest.invoke()?.cancelConclusion != null ||
|
||||||
|| state.sasTransactionState is VerificationTxState.TerminalTxState
|
state.sasTransactionState is VerificationTxState.TerminalTxState ||
|
||||||
|| state.verifyingFrom4S) {
|
state.verifyingFrom4S) {
|
||||||
// you cannot cancel anymore
|
// you cannot cancel anymore
|
||||||
} else {
|
} else {
|
||||||
setState {
|
setState {
|
||||||
@ -537,9 +537,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pr.localId == state.pendingLocalId
|
if (pr.localId == state.pendingLocalId ||
|
||||||
|| pr.localId == state.pendingRequest.invoke()?.localId
|
pr.localId == state.pendingRequest.invoke()?.localId ||
|
||||||
|| state.pendingRequest.invoke()?.transactionId == pr.transactionId) {
|
state.pendingRequest.invoke()?.transactionId == pr.transactionId) {
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
transactionId = args.verificationId ?: pr.transactionId,
|
transactionId = args.verificationId ?: pr.transactionId,
|
||||||
|
@ -194,8 +194,8 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto
|
|||||||
state.displayMode is RoomDevToolViewState.Mode.StateEventDetail
|
state.displayMode is RoomDevToolViewState.Mode.StateEventDetail
|
||||||
}
|
}
|
||||||
R.id.menuItemSend -> {
|
R.id.menuItemSend -> {
|
||||||
state.displayMode is RoomDevToolViewState.Mode.EditEventContent
|
state.displayMode is RoomDevToolViewState.Mode.EditEventContent ||
|
||||||
|| state.displayMode is RoomDevToolViewState.Mode.SendEventForm
|
state.displayMode is RoomDevToolViewState.Mode.SendEventForm
|
||||||
}
|
}
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
@ -282,8 +282,8 @@ class DiscoverySettingsController @Inject constructor(
|
|||||||
val error = pidInfo.finalRequest.error
|
val error = pidInfo.finalRequest.error
|
||||||
// Deal with error 500
|
// Deal with error 500
|
||||||
// Ref: https://github.com/matrix-org/sydent/issues/292
|
// Ref: https://github.com/matrix-org/sydent/issues/292
|
||||||
if (error is Failure.ServerError
|
if (error is Failure.ServerError &&
|
||||||
&& error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) {
|
error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) {
|
||||||
stringProvider.getString(R.string.settings_text_message_sent_wrong_code)
|
stringProvider.getString(R.string.settings_text_message_sent_wrong_code)
|
||||||
} else {
|
} else {
|
||||||
errorFormatter.toHumanReadable(error)
|
errorFormatter.toHumanReadable(error)
|
||||||
|
@ -254,8 +254,8 @@ class HomeActivity :
|
|||||||
|
|
||||||
if (!vectorPreferences.didPromoteNewRestrictedFeature()) {
|
if (!vectorPreferences.didPromoteNewRestrictedFeature()) {
|
||||||
promoteRestrictedViewModel.subscribe(this) {
|
promoteRestrictedViewModel.subscribe(this) {
|
||||||
if (it.activeSpaceSummary != null && !it.activeSpaceSummary.isPublic
|
if (it.activeSpaceSummary != null && !it.activeSpaceSummary.isPublic &&
|
||||||
&& it.activeSpaceSummary.otherMemberIds.isNotEmpty()) {
|
it.activeSpaceSummary.otherMemberIds.isNotEmpty()) {
|
||||||
// It's a private space with some members show this once
|
// It's a private space with some members show this once
|
||||||
if (it.canUserManageSpace && !popupAlertManager.hasAlertsToShow()) {
|
if (it.canUserManageSpace && !popupAlertManager.hasAlertsToShow()) {
|
||||||
if (!vectorPreferences.didPromoteNewRestrictedFeature()) {
|
if (!vectorPreferences.didPromoteNewRestrictedFeature()) {
|
||||||
@ -298,8 +298,8 @@ class HomeActivity :
|
|||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe { isHandled ->
|
.subscribe { isHandled ->
|
||||||
if (!isHandled) {
|
if (!isHandled) {
|
||||||
val isMatrixToLink = deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE)
|
val isMatrixToLink = deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) ||
|
||||||
|| deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE)
|
deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE)
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.dialog_title_error)
|
.setTitle(R.string.dialog_title_error)
|
||||||
.setMessage(if (isMatrixToLink) R.string.permalink_malformed else R.string.universal_link_malformed)
|
.setMessage(if (isMatrixToLink) R.string.permalink_malformed else R.string.universal_link_malformed)
|
||||||
|
@ -218,9 +218,9 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||||||
object : UserInteractiveAuthInterceptor {
|
object : UserInteractiveAuthInterceptor {
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
// We missed server grace period or it's not setup, see if we remember locally password
|
// We missed server grace period or it's not setup, see if we remember locally password
|
||||||
if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD
|
if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD &&
|
||||||
&& errCode == null
|
errCode == null &&
|
||||||
&& reAuthHelper.data != null) {
|
reAuthHelper.data != null) {
|
||||||
promise.resume(
|
promise.resume(
|
||||||
UserPasswordAuth(
|
UserPasswordAuth(
|
||||||
session = flowResponse.session,
|
session = flowResponse.session,
|
||||||
|
@ -177,12 +177,12 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia
|
|||||||
CountInfo(
|
CountInfo(
|
||||||
homeCount = counts,
|
homeCount = counts,
|
||||||
otherCount = RoomAggregateNotificationCount(
|
otherCount = RoomAggregateNotificationCount(
|
||||||
notificationCount = rootCounts.fold(0, { acc, rs -> acc + rs.notificationCount })
|
notificationCount = rootCounts.fold(0, { acc, rs -> acc + rs.notificationCount }) +
|
||||||
+ (counts.notificationCount.takeIf { selectedSpace != null } ?: 0)
|
(counts.notificationCount.takeIf { selectedSpace != null } ?: 0) +
|
||||||
+ spaceInviteCount,
|
spaceInviteCount,
|
||||||
highlightCount = rootCounts.fold(0, { acc, rs -> acc + rs.highlightCount })
|
highlightCount = rootCounts.fold(0, { acc, rs -> acc + rs.highlightCount }) +
|
||||||
+ (counts.highlightCount.takeIf { selectedSpace != null } ?: 0)
|
(counts.highlightCount.takeIf { selectedSpace != null } ?: 0) +
|
||||||
+ spaceInviteCount
|
spaceInviteCount
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -105,8 +105,8 @@ class ChatEffectManager @Inject constructor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun hasAlreadyPlayed(event: TimelineEvent): Boolean {
|
private fun hasAlreadyPlayed(event: TimelineEvent): Boolean {
|
||||||
return alreadyPlayed.contains(event.eventId)
|
return alreadyPlayed.contains(event.eventId) ||
|
||||||
|| (event.root.unsignedData?.transactionId?.let { alreadyPlayed.contains(it) } ?: false)
|
(event.root.unsignedData?.transactionId?.let { alreadyPlayed.contains(it) } ?: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findEffect(content: MessageContent, event: TimelineEvent): ChatEffect? {
|
private fun findEffect(content: MessageContent, event: TimelineEvent): ChatEffect? {
|
||||||
|
@ -1279,10 +1279,10 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
// Add external keyboard functionality (to send messages)
|
// Add external keyboard functionality (to send messages)
|
||||||
else if (null != keyEvent
|
else if (null != keyEvent &&
|
||||||
&& !keyEvent.isShiftPressed
|
!keyEvent.isShiftPressed &&
|
||||||
&& keyEvent.keyCode == KeyEvent.KEYCODE_ENTER
|
keyEvent.keyCode == KeyEvent.KEYCODE_ENTER &&
|
||||||
&& resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS) {
|
resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS) {
|
||||||
sendTextMessage(v.text)
|
sendTextMessage(v.text)
|
||||||
true
|
true
|
||||||
} else false
|
} else false
|
||||||
@ -1865,8 +1865,8 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onSaveActionClicked(action: EventSharedAction.Save) {
|
private fun onSaveActionClicked(action: EventSharedAction.Save) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
|
||||||
&& !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) {
|
!checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) {
|
||||||
sharedActionViewModel.pendingAction = action
|
sharedActionViewModel.pendingAction = action
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2018,8 +2018,8 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
private fun insertUserDisplayNameInTextEditor(userId: String) {
|
private fun insertUserDisplayNameInTextEditor(userId: String) {
|
||||||
val startToCompose = views.composerLayout.text.isNullOrBlank()
|
val startToCompose = views.composerLayout.text.isNullOrBlank()
|
||||||
|
|
||||||
if (startToCompose
|
if (startToCompose &&
|
||||||
&& userId == session.myUserId) {
|
userId == session.myUserId) {
|
||||||
// Empty composer, current user: start an emote
|
// Empty composer, current user: start an emote
|
||||||
views.composerLayout.views.composerEditText.setText(Command.EMOTE.command + " ")
|
views.composerLayout.views.composerEditText.setText(Command.EMOTE.command + " ")
|
||||||
views.composerLayout.views.composerEditText.setSelection(Command.EMOTE.length)
|
views.composerLayout.views.composerEditText.setSelection(Command.EMOTE.length)
|
||||||
|
@ -550,8 +550,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
val widget = action.widget
|
val widget = action.widget
|
||||||
val domain = action.widget.widgetContent.data["domain"] as? String ?: ""
|
val domain = action.widget.widgetContent.data["domain"] as? String ?: ""
|
||||||
val isAllowed = action.userJustAccepted || if (widget.type == WidgetType.Jitsi) {
|
val isAllowed = action.userJustAccepted || if (widget.type == WidgetType.Jitsi) {
|
||||||
widget.senderInfo?.userId == session.myUserId
|
widget.senderInfo?.userId == session.myUserId ||
|
||||||
|| session.integrationManagerService().isNativeWidgetDomainAllowed(
|
session.integrationManagerService().isNativeWidgetDomainAllowed(
|
||||||
action.widget.type.preferred,
|
action.widget.type.preferred,
|
||||||
domain
|
domain
|
||||||
)
|
)
|
||||||
@ -1270,8 +1270,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) {
|
private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) {
|
||||||
val mxcUrl = action.messageFileContent.getFileUrl() ?: return
|
val mxcUrl = action.messageFileContent.getFileUrl() ?: return
|
||||||
val isLocalSendingFile = action.senderId == session.myUserId
|
val isLocalSendingFile = action.senderId == session.myUserId &&
|
||||||
&& mxcUrl.startsWith("content://")
|
mxcUrl.startsWith("content://")
|
||||||
if (isLocalSendingFile) {
|
if (isLocalSendingFile) {
|
||||||
tryOrNull { Uri.parse(mxcUrl) }?.let {
|
tryOrNull { Uri.parse(mxcUrl) }?.let {
|
||||||
_viewEvents.post(RoomDetailViewEvents.OpenFile(
|
_viewEvents.post(RoomDetailViewEvents.OpenFile(
|
||||||
|
@ -260,8 +260,8 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li
|
|||||||
val previousRecordingState = recordingState
|
val previousRecordingState = recordingState
|
||||||
if (recordingState == RecordingState.STARTED) {
|
if (recordingState == RecordingState.STARTED) {
|
||||||
// Determine if cancelling or locking for the first move action.
|
// Determine if cancelling or locking for the first move action.
|
||||||
if (((currentX < firstX && rtlXMultiplier == 1) || (currentX > firstX && rtlXMultiplier == -1))
|
if (((currentX < firstX && rtlXMultiplier == 1) || (currentX > firstX && rtlXMultiplier == -1)) &&
|
||||||
&& distanceX > distanceY && distanceX > lastDistanceX) {
|
distanceX > distanceY && distanceX > lastDistanceX) {
|
||||||
recordingState = RecordingState.CANCELLING
|
recordingState = RecordingState.CANCELLING
|
||||||
} else if (currentY < firstY && distanceY > distanceX && distanceY > lastDistanceY) {
|
} else if (currentY < firstY && distanceY > distanceX && distanceY > lastDistanceY) {
|
||||||
recordingState = RecordingState.LOCKING
|
recordingState = RecordingState.LOCKING
|
||||||
|
@ -249,8 +249,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
if (partialState.highlightedEventId != newPartialState.highlightedEventId) {
|
if (partialState.highlightedEventId != newPartialState.highlightedEventId) {
|
||||||
// Clear cache to force a refresh
|
// Clear cache to force a refresh
|
||||||
for (i in 0 until modelCache.size) {
|
for (i in 0 until modelCache.size) {
|
||||||
if (modelCache[i]?.eventId == viewState.highlightedEventId
|
if (modelCache[i]?.eventId == viewState.highlightedEventId ||
|
||||||
|| modelCache[i]?.eventId == partialState.highlightedEventId) {
|
modelCache[i]?.eventId == partialState.highlightedEventId) {
|
||||||
modelCache[i] = null
|
modelCache[i] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,8 +498,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
if (vectorPreferences.labShowCompleteHistoryInEncryptedRoom()) {
|
if (vectorPreferences.labShowCompleteHistoryInEncryptedRoom()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (event.root.type == EventType.STATE_ROOM_MEMBER
|
if (event.root.type == EventType.STATE_ROOM_MEMBER &&
|
||||||
&& event.root.stateKey == session.myUserId) {
|
event.root.stateKey == session.myUserId) {
|
||||||
val content = event.root.content.toModel<RoomMemberContent>()
|
val content = event.root.content.toModel<RoomMemberContent>()
|
||||||
if (content?.membership == Membership.INVITE) {
|
if (content?.membership == Membership.INVITE) {
|
||||||
hasReachedInvite = true
|
hasReachedInvite = true
|
||||||
|
@ -365,14 +365,14 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||||||
if (vectorPreferences.developerMode()) {
|
if (vectorPreferences.developerMode()) {
|
||||||
if (timelineEvent.isEncrypted() && timelineEvent.root.mCryptoError != null) {
|
if (timelineEvent.isEncrypted() && timelineEvent.root.mCryptoError != null) {
|
||||||
val keysBackupService = session.cryptoService().keysBackupService()
|
val keysBackupService = session.cryptoService().keysBackupService()
|
||||||
if (keysBackupService.state == KeysBackupState.NotTrusted
|
if (keysBackupService.state == KeysBackupState.NotTrusted ||
|
||||||
|| (keysBackupService.state == KeysBackupState.ReadyToBackUp
|
(keysBackupService.state == KeysBackupState.ReadyToBackUp &&
|
||||||
&& keysBackupService.canRestoreKeys())
|
keysBackupService.canRestoreKeys())
|
||||||
) {
|
) {
|
||||||
add(EventSharedAction.UseKeyBackup)
|
add(EventSharedAction.UseKeyBackup)
|
||||||
}
|
}
|
||||||
if (session.cryptoService().getCryptoDeviceInfo(session.myUserId).size > 1
|
if (session.cryptoService().getCryptoDeviceInfo(session.myUserId).size > 1 ||
|
||||||
|| timelineEvent.senderInfo.userId != session.myUserId) {
|
timelineEvent.senderInfo.userId != session.myUserId) {
|
||||||
add(EventSharedAction.ReRequestKey(timelineEvent.eventId))
|
add(EventSharedAction.ReRequestKey(timelineEvent.eventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -435,9 +435,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun canRetry(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
|
private fun canRetry(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
|
||||||
return event.root.sendState.hasFailed()
|
return event.root.sendState.hasFailed() &&
|
||||||
&& actionPermissions.canSendMessage
|
actionPermissions.canSendMessage &&
|
||||||
&& (event.root.isAttachmentMessage() || event.root.isTextMessage())
|
(event.root.isAttachmentMessage() || event.root.isTextMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun canViewReactions(event: TimelineEvent): Boolean {
|
private fun canViewReactions(event: TimelineEvent): Boolean {
|
||||||
@ -453,8 +453,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||||||
// TODO if user is admin or moderator
|
// TODO if user is admin or moderator
|
||||||
val messageContent = event.root.getClearContent().toModel<MessageContent>()
|
val messageContent = event.root.getClearContent().toModel<MessageContent>()
|
||||||
return event.root.senderId == myUserId && (
|
return event.root.senderId == myUserId && (
|
||||||
messageContent?.msgType == MessageType.MSGTYPE_TEXT
|
messageContent?.msgType == MessageType.MSGTYPE_TEXT ||
|
||||||
|| messageContent?.msgType == MessageType.MSGTYPE_EMOTE
|
messageContent?.msgType == MessageType.MSGTYPE_EMOTE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user