Merge remote-tracking branch 'upstream/develop' into direct_share
This commit is contained in:
commit
afe55ae57e
11
.idea/codeStyles/Project.xml
generated
11
.idea/codeStyles/Project.xml
generated
@ -4,16 +4,7 @@
|
|||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
<value>
|
<value>
|
||||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
|
||||||
<value>
|
|
||||||
<package name="" alias="false" withSubpackages="true" />
|
|
||||||
<package name="java" alias="false" withSubpackages="true" />
|
|
||||||
<package name="javax" alias="false" withSubpackages="true" />
|
|
||||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
|
||||||
<package name="" alias="true" withSubpackages="true" />
|
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
|
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
|
||||||
|
@ -18,8 +18,10 @@ Improvements 🙌:
|
|||||||
- Filter room member (and banned users) by name (#2184)
|
- Filter room member (and banned users) by name (#2184)
|
||||||
- Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
|
- Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
|
||||||
- Direct share (#2029)
|
- Direct share (#2029)
|
||||||
|
- Add FAB to room members list (#2226)
|
||||||
- Add Sygnal API implementation to test is Push are correctly received
|
- Add Sygnal API implementation to test is Push are correctly received
|
||||||
- Add PushGateway API implementation to test if Push are correctly received
|
- Add PushGateway API implementation to test if Push are correctly received
|
||||||
|
- Cross signing: shouldn't offer to verify with other session when there is not. (#2227)
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
||||||
@ -27,9 +29,10 @@ Bugfix 🐛:
|
|||||||
- Invalid popup when pressing back (#1635)
|
- Invalid popup when pressing back (#1635)
|
||||||
- Simplifies draft management and should fix bunch of draft issues (#952, #683)
|
- Simplifies draft management and should fix bunch of draft issues (#952, #683)
|
||||||
- Very long topic cannot be fully visible (#1957)
|
- Very long topic cannot be fully visible (#1957)
|
||||||
|
- Properly detect cross signing keys reset
|
||||||
|
|
||||||
Translations 🗣:
|
Translations 🗣:
|
||||||
-
|
- Move store data to `/fastlane/metadata/android` (#812)
|
||||||
|
|
||||||
SDK API changes ⚠️:
|
SDK API changes ⚠️:
|
||||||
- Search messages in a room by using Session.searchService() or Room.search()
|
- Search messages in a room by using Session.searchService() or Room.search()
|
||||||
|
1
fastlane/metadata/android/pt_BR/title.txt
Normal file
1
fastlane/metadata/android/pt_BR/title.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Element (o novo Riot.im)
|
@ -13,7 +13,7 @@ Element здатен забезпечити усе це завдяки тому,
|
|||||||
|
|
||||||
Element надає вам повний контроль, дозволяючи обирати з-поміж надавачів послуг, що обслуговують сервери з вашими бесідами. Ви вільні обрати будь-який спосіб розміщення прямо з застосунку Element:
|
Element надає вам повний контроль, дозволяючи обирати з-поміж надавачів послуг, що обслуговують сервери з вашими бесідами. Ви вільні обрати будь-який спосіб розміщення прямо з застосунку Element:
|
||||||
|
|
||||||
1. Отримати безкоштовний обліковий запис на загальнодоступному сервері matrix.org
|
1. Отримати безкоштовний обліковий запис на загальнодоступному сервері matrix.org, який обслуговують розробники Matrix, чи на одному з тисяч публічних серверів, які обслуговують волонтери
|
||||||
2. Розмістити свій обліковий запис на власному сервері
|
2. Розмістити свій обліковий запис на власному сервері
|
||||||
3. Зареєструватись на індивідуальному сервері, просто підписавшись на послуги платформи Element Matrix Services
|
3. Зареєструватись на індивідуальному сервері, просто підписавшись на послуги платформи Element Matrix Services
|
||||||
|
|
@ -17,6 +17,8 @@
|
|||||||
package org.matrix.android.sdk.api
|
package org.matrix.android.sdk.api
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
@ -48,13 +50,17 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
|||||||
@Inject internal lateinit var olmManager: OlmManager
|
@Inject internal lateinit var olmManager: OlmManager
|
||||||
@Inject internal lateinit var sessionManager: SessionManager
|
@Inject internal lateinit var sessionManager: SessionManager
|
||||||
|
|
||||||
|
private val uiHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Monarchy.init(context)
|
Monarchy.init(context)
|
||||||
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
|
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
|
||||||
if (context.applicationContext !is Configuration.Provider) {
|
if (context.applicationContext !is Configuration.Provider) {
|
||||||
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
|
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
|
||||||
}
|
}
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
|
uiHandler.post {
|
||||||
|
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUserAgent() = userAgentHolder.userAgent
|
fun getUserAgent() = userAgentHolder.userAgent
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto
|
package org.matrix.android.sdk.internal.crypto
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
@ -34,14 +33,8 @@ internal class CryptoStoreHelper {
|
|||||||
.modules(RealmCryptoStoreModule())
|
.modules(RealmCryptoStoreModule())
|
||||||
.build(),
|
.build(),
|
||||||
crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()),
|
crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()),
|
||||||
credentials = createCredential())
|
userId = "userId_" + Random.nextInt(),
|
||||||
|
deviceId = "deviceId_sample"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createCredential() = Credentials(
|
|
||||||
userId = "userId_" + Random.nextInt(),
|
|
||||||
homeServer = "http://matrix.org",
|
|
||||||
accessToken = "access_token",
|
|
||||||
refreshToken = null,
|
|
||||||
deviceId = "deviceId_sample"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.crypto.encryption
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import org.amshove.kluent.shouldBe
|
||||||
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.MethodSorters
|
||||||
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
|
import org.matrix.android.sdk.api.NoOpMatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
|
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||||
|
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
class EncryptionTest : InstrumentedTest {
|
||||||
|
private val mTestHelper = CommonTestHelper(context())
|
||||||
|
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_EncryptionEvent() {
|
||||||
|
performTest(roomShouldBeEncrypted = false) { room ->
|
||||||
|
// Send an encryption Event as an Event (and not as a state event)
|
||||||
|
room.sendEvent(
|
||||||
|
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||||
|
content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_EncryptionStateEvent() {
|
||||||
|
performTest(roomShouldBeEncrypted = true) { room ->
|
||||||
|
// Send an encryption Event as a State Event
|
||||||
|
room.sendStateEvent(
|
||||||
|
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||||
|
stateKey = null,
|
||||||
|
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent(),
|
||||||
|
callback = NoOpMatrixCallback()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
|
||||||
|
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
|
||||||
|
|
||||||
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
val room = aliceSession.getRoom(cryptoTestData.roomId)!!
|
||||||
|
|
||||||
|
room.isEncrypted() shouldBe false
|
||||||
|
|
||||||
|
val timeline = room.createTimeline(null, TimelineSettings(10))
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
val timelineListener = object : Timeline.Listener {
|
||||||
|
override fun onTimelineFailure(throwable: Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewTimelineEvents(eventIds: List<String>) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
||||||
|
val newMessages = snapshot
|
||||||
|
.filter { it.root.sendState == SendState.SYNCED }
|
||||||
|
.filter { it.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION }
|
||||||
|
|
||||||
|
if (newMessages.isNotEmpty()) {
|
||||||
|
timeline.removeListener(this)
|
||||||
|
latch.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeline.start()
|
||||||
|
timeline.addListener(timelineListener)
|
||||||
|
|
||||||
|
action.invoke(room)
|
||||||
|
|
||||||
|
mTestHelper.await(latch)
|
||||||
|
timeline.dispose()
|
||||||
|
|
||||||
|
room.isEncrypted() shouldBe roomShouldBeEncrypted
|
||||||
|
|
||||||
|
cryptoTestData.cleanUp(mTestHelper)
|
||||||
|
}
|
||||||
|
}
|
@ -17,15 +17,14 @@
|
|||||||
package org.matrix.android.sdk.internal.crypto.verification.qrcode
|
package org.matrix.android.sdk.internal.crypto.verification.qrcode
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.amshove.kluent.shouldBeNull
|
import org.amshove.kluent.shouldBeNull
|
||||||
import org.amshove.kluent.shouldEqual
|
|
||||||
import org.amshove.kluent.shouldEqualTo
|
|
||||||
import org.amshove.kluent.shouldNotBeNull
|
import org.amshove.kluent.shouldNotBeNull
|
||||||
import org.junit.FixMethodOrder
|
import org.junit.FixMethodOrder
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.MethodSorters
|
import org.junit.runners.MethodSorters
|
||||||
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
@ -66,32 +65,32 @@ class QrCodeTest : InstrumentedTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEncoding1() {
|
fun testEncoding1() {
|
||||||
qrCode1.toEncodedString() shouldEqual value1
|
qrCode1.toEncodedString() shouldBeEqualTo value1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEncoding2() {
|
fun testEncoding2() {
|
||||||
qrCode2.toEncodedString() shouldEqual value2
|
qrCode2.toEncodedString() shouldBeEqualTo value2
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEncoding3() {
|
fun testEncoding3() {
|
||||||
qrCode3.toEncodedString() shouldEqual value3
|
qrCode3.toEncodedString() shouldBeEqualTo value3
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSymmetry1() {
|
fun testSymmetry1() {
|
||||||
qrCode1.toEncodedString().toQrCodeData() shouldEqual qrCode1
|
qrCode1.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSymmetry2() {
|
fun testSymmetry2() {
|
||||||
qrCode2.toEncodedString().toQrCodeData() shouldEqual qrCode2
|
qrCode2.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode2
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSymmetry3() {
|
fun testSymmetry3() {
|
||||||
qrCode3.toEncodedString().toQrCodeData() shouldEqual qrCode3
|
qrCode3.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode3
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -102,7 +101,7 @@ class QrCodeTest : InstrumentedTest {
|
|||||||
checkHeader(byteArray)
|
checkHeader(byteArray)
|
||||||
|
|
||||||
// Mode
|
// Mode
|
||||||
byteArray[7] shouldEqualTo 0
|
byteArray[7] shouldBeEqualTo 0
|
||||||
|
|
||||||
checkSizeAndTransaction(byteArray)
|
checkSizeAndTransaction(byteArray)
|
||||||
|
|
||||||
@ -120,7 +119,7 @@ class QrCodeTest : InstrumentedTest {
|
|||||||
checkHeader(byteArray)
|
checkHeader(byteArray)
|
||||||
|
|
||||||
// Mode
|
// Mode
|
||||||
byteArray[7] shouldEqualTo 1
|
byteArray[7] shouldBeEqualTo 1
|
||||||
|
|
||||||
checkSizeAndTransaction(byteArray)
|
checkSizeAndTransaction(byteArray)
|
||||||
compareArray(byteArray.copyOfRange(23, 23 + 32), kte_byteArray)
|
compareArray(byteArray.copyOfRange(23, 23 + 32), kte_byteArray)
|
||||||
@ -137,7 +136,7 @@ class QrCodeTest : InstrumentedTest {
|
|||||||
checkHeader(byteArray)
|
checkHeader(byteArray)
|
||||||
|
|
||||||
// Mode
|
// Mode
|
||||||
byteArray[7] shouldEqualTo 2
|
byteArray[7] shouldBeEqualTo 2
|
||||||
|
|
||||||
checkSizeAndTransaction(byteArray)
|
checkSizeAndTransaction(byteArray)
|
||||||
compareArray(byteArray.copyOfRange(23, 23 + 32), tlx_byteArray)
|
compareArray(byteArray.copyOfRange(23, 23 + 32), tlx_byteArray)
|
||||||
@ -156,10 +155,10 @@ class QrCodeTest : InstrumentedTest {
|
|||||||
val result = qrCode.toEncodedString()
|
val result = qrCode.toEncodedString()
|
||||||
val expected = value1.replace("\u0000\u000DMaTransaction", "\u0007\u00D0$longTransactionId")
|
val expected = value1.replace("\u0000\u000DMaTransaction", "\u0007\u00D0$longTransactionId")
|
||||||
|
|
||||||
result shouldEqual expected
|
result shouldBeEqualTo expected
|
||||||
|
|
||||||
// Reverse operation
|
// Reverse operation
|
||||||
expected.toQrCodeData() shouldEqual qrCode
|
expected.toQrCodeData() shouldBeEqualTo qrCode
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -170,7 +169,7 @@ class QrCodeTest : InstrumentedTest {
|
|||||||
val qrCode = qrCode1.copy(transactionId = longTransactionId)
|
val qrCode = qrCode1.copy(transactionId = longTransactionId)
|
||||||
|
|
||||||
// Symmetric operation
|
// Symmetric operation
|
||||||
qrCode.toEncodedString().toQrCodeData() shouldEqual qrCode
|
qrCode.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,32 +217,32 @@ class QrCodeTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun compareArray(actual: ByteArray, expected: ByteArray) {
|
private fun compareArray(actual: ByteArray, expected: ByteArray) {
|
||||||
actual.size shouldEqual expected.size
|
actual.size shouldBeEqualTo expected.size
|
||||||
|
|
||||||
for (i in actual.indices) {
|
for (i in actual.indices) {
|
||||||
actual[i] shouldEqualTo expected[i]
|
actual[i] shouldBeEqualTo expected[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkHeader(byteArray: ByteArray) {
|
private fun checkHeader(byteArray: ByteArray) {
|
||||||
// MATRIX
|
// MATRIX
|
||||||
byteArray[0] shouldEqualTo 'M'.toByte()
|
byteArray[0] shouldBeEqualTo 'M'.toByte()
|
||||||
byteArray[1] shouldEqualTo 'A'.toByte()
|
byteArray[1] shouldBeEqualTo 'A'.toByte()
|
||||||
byteArray[2] shouldEqualTo 'T'.toByte()
|
byteArray[2] shouldBeEqualTo 'T'.toByte()
|
||||||
byteArray[3] shouldEqualTo 'R'.toByte()
|
byteArray[3] shouldBeEqualTo 'R'.toByte()
|
||||||
byteArray[4] shouldEqualTo 'I'.toByte()
|
byteArray[4] shouldBeEqualTo 'I'.toByte()
|
||||||
byteArray[5] shouldEqualTo 'X'.toByte()
|
byteArray[5] shouldBeEqualTo 'X'.toByte()
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
byteArray[6] shouldEqualTo 2
|
byteArray[6] shouldBeEqualTo 2
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkSizeAndTransaction(byteArray: ByteArray) {
|
private fun checkSizeAndTransaction(byteArray: ByteArray) {
|
||||||
// Size
|
// Size
|
||||||
byteArray[8] shouldEqualTo 0
|
byteArray[8] shouldBeEqualTo 0
|
||||||
byteArray[9] shouldEqualTo 13
|
byteArray[9] shouldBeEqualTo 13
|
||||||
|
|
||||||
// Transaction
|
// Transaction
|
||||||
byteArray.copyOfRange(10, 10 + "MaTransaction".length).toString(Charsets.ISO_8859_1) shouldEqual "MaTransaction"
|
byteArray.copyOfRange(10, 10 + "MaTransaction".length).toString(Charsets.ISO_8859_1) shouldBeEqualTo "MaTransaction"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,14 @@ package org.matrix.android.sdk.session.room.timeline
|
|||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import io.realm.kotlin.createObject
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.amshove.kluent.shouldBeTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
@ -29,14 +37,6 @@ import org.matrix.android.sdk.internal.database.model.SessionRealmModule
|
|||||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||||
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeListOfEvents
|
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeListOfEvents
|
||||||
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMessageEvent
|
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMessageEvent
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmConfiguration
|
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
import org.amshove.kluent.shouldBeTrue
|
|
||||||
import org.amshove.kluent.shouldEqual
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
internal class ChunkEntityTest : InstrumentedTest {
|
internal class ChunkEntityTest : InstrumentedTest {
|
||||||
@ -60,10 +60,10 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
val chunk: ChunkEntity = realm.createObject()
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
|
|
||||||
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
|
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
|
||||||
realm.copyToRealmOrUpdate(it)
|
realm.copyToRealm(it)
|
||||||
}
|
}
|
||||||
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
|
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
|
||||||
chunk.timelineEvents.size shouldEqual 1
|
chunk.timelineEvents.size shouldBeEqualTo 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,11 +72,11 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
val chunk: ChunkEntity = realm.createObject()
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
|
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
|
||||||
realm.copyToRealmOrUpdate(it)
|
realm.copyToRealm(it)
|
||||||
}
|
}
|
||||||
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
|
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
|
||||||
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
|
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
|
||||||
chunk.timelineEvents.size shouldEqual 1
|
chunk.timelineEvents.size shouldBeEqualTo 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
|
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.timelineEvents.size shouldEqual 60
|
chunk1.timelineEvents.size shouldBeEqualTo 60
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
chunk1.addAll(ROOM_ID, eventsForChunk1, PaginationDirection.FORWARDS)
|
chunk1.addAll(ROOM_ID, eventsForChunk1, PaginationDirection.FORWARDS)
|
||||||
chunk2.addAll(ROOM_ID, eventsForChunk2, PaginationDirection.BACKWARDS)
|
chunk2.addAll(ROOM_ID, eventsForChunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
|
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.timelineEvents.size shouldEqual 40
|
chunk1.timelineEvents.size shouldBeEqualTo 40
|
||||||
chunk1.isLastForward.shouldBeTrue()
|
chunk1.isLastForward.shouldBeTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.FORWARDS)
|
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.FORWARDS)
|
||||||
chunk1.prevToken shouldEqual prevToken
|
chunk1.prevToken shouldBeEqualTo prevToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
|
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.nextToken shouldEqual nextToken
|
chunk1.nextToken shouldBeEqualTo nextToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
direction: PaginationDirection) {
|
direction: PaginationDirection) {
|
||||||
events.forEach { event ->
|
events.forEach { event ->
|
||||||
val fakeEvent = event.toEntity(roomId, SendState.SYNCED, System.currentTimeMillis()).let {
|
val fakeEvent = event.toEntity(roomId, SendState.SYNCED, System.currentTimeMillis()).let {
|
||||||
realm.copyToRealmOrUpdate(it)
|
realm.copyToRealm(it)
|
||||||
}
|
}
|
||||||
addTimelineEvent(roomId, fakeEvent, direction, emptyMap())
|
addTimelineEvent(roomId, fakeEvent, direction, emptyMap())
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
|
|||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
@ -41,11 +41,11 @@ object RoomDataHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createFakeEvent(type: String,
|
private fun createFakeEvent(type: String,
|
||||||
content: Content? = null,
|
content: Content? = null,
|
||||||
prevContent: Content? = null,
|
prevContent: Content? = null,
|
||||||
sender: String = FAKE_TEST_SENDER,
|
sender: String = FAKE_TEST_SENDER,
|
||||||
stateKey: String = FAKE_TEST_SENDER
|
stateKey: String = FAKE_TEST_SENDER
|
||||||
): Event {
|
): Event {
|
||||||
return Event(
|
return Event(
|
||||||
type = type,
|
type = type,
|
||||||
@ -62,8 +62,8 @@ object RoomDataHelper {
|
|||||||
return createFakeEvent(EventType.MESSAGE, message)
|
return createFakeEvent(EventType.MESSAGE, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createFakeRoomMemberEvent(): Event {
|
private fun createFakeRoomMemberEvent(): Event {
|
||||||
val roomMember = RoomMemberSummary(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
|
val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
|
||||||
return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
|
return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ internal class TimelineTest : InstrumentedTest {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// latch.await()
|
// latch.await()
|
||||||
// timelineEvents.size shouldEqual initialLoad + paginationCount
|
// timelineEvents.size shouldBeEqualTo initialLoad + paginationCount
|
||||||
// timeline.dispose()
|
// timeline.dispose()
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="org.matrix.android.sdk">
|
package="org.matrix.android.sdk">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<!-- TODO Is WRITE_EXTERNAL_STORAGE necessary? -->
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application android:networkSecurityConfig="@xml/network_security_config">
|
<application android:networkSecurityConfig="@xml/network_security_config">
|
||||||
|
@ -42,4 +42,4 @@ interface MatrixCallback<in T> {
|
|||||||
/**
|
/**
|
||||||
* Basic no op implementation
|
* Basic no op implementation
|
||||||
*/
|
*/
|
||||||
class NoOpMatrixCallback<T>: MatrixCallback<T>
|
class NoOpMatrixCallback<T> : MatrixCallback<T>
|
||||||
|
@ -48,18 +48,25 @@ data class MatrixError(
|
|||||||
companion object {
|
companion object {
|
||||||
/** Forbidden access, e.g. joining a room without permission, failed login. */
|
/** Forbidden access, e.g. joining a room without permission, failed login. */
|
||||||
const val M_FORBIDDEN = "M_FORBIDDEN"
|
const val M_FORBIDDEN = "M_FORBIDDEN"
|
||||||
|
|
||||||
/** An unknown error has occurred. */
|
/** An unknown error has occurred. */
|
||||||
const val M_UNKNOWN = "M_UNKNOWN"
|
const val M_UNKNOWN = "M_UNKNOWN"
|
||||||
|
|
||||||
/** The access token specified was not recognised. */
|
/** The access token specified was not recognised. */
|
||||||
const val M_UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN"
|
const val M_UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN"
|
||||||
|
|
||||||
/** No access token was specified for the request. */
|
/** No access token was specified for the request. */
|
||||||
const val M_MISSING_TOKEN = "M_MISSING_TOKEN"
|
const val M_MISSING_TOKEN = "M_MISSING_TOKEN"
|
||||||
|
|
||||||
/** Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys. */
|
/** Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys. */
|
||||||
const val M_BAD_JSON = "M_BAD_JSON"
|
const val M_BAD_JSON = "M_BAD_JSON"
|
||||||
|
|
||||||
/** Request did not contain valid JSON. */
|
/** Request did not contain valid JSON. */
|
||||||
const val M_NOT_JSON = "M_NOT_JSON"
|
const val M_NOT_JSON = "M_NOT_JSON"
|
||||||
|
|
||||||
/** No resource was found for this request. */
|
/** No resource was found for this request. */
|
||||||
const val M_NOT_FOUND = "M_NOT_FOUND"
|
const val M_NOT_FOUND = "M_NOT_FOUND"
|
||||||
|
|
||||||
/** Too many requests have been sent in a short period of time. Wait a while then try again. */
|
/** Too many requests have been sent in a short period of time. Wait a while then try again. */
|
||||||
const val M_LIMIT_EXCEEDED = "M_LIMIT_EXCEEDED"
|
const val M_LIMIT_EXCEEDED = "M_LIMIT_EXCEEDED"
|
||||||
|
|
||||||
@ -69,68 +76,97 @@ data class MatrixError(
|
|||||||
|
|
||||||
/** Encountered when trying to register a user ID which has been taken. */
|
/** Encountered when trying to register a user ID which has been taken. */
|
||||||
const val M_USER_IN_USE = "M_USER_IN_USE"
|
const val M_USER_IN_USE = "M_USER_IN_USE"
|
||||||
|
|
||||||
/** Sent when the room alias given to the createRoom API is already in use. */
|
/** Sent when the room alias given to the createRoom API is already in use. */
|
||||||
const val M_ROOM_IN_USE = "M_ROOM_IN_USE"
|
const val M_ROOM_IN_USE = "M_ROOM_IN_USE"
|
||||||
|
|
||||||
/** (Not documented yet) */
|
/** (Not documented yet) */
|
||||||
const val M_BAD_PAGINATION = "M_BAD_PAGINATION"
|
const val M_BAD_PAGINATION = "M_BAD_PAGINATION"
|
||||||
|
|
||||||
/** The request was not correctly authorized. Usually due to login failures. */
|
/** The request was not correctly authorized. Usually due to login failures. */
|
||||||
const val M_UNAUTHORIZED = "M_UNAUTHORIZED"
|
const val M_UNAUTHORIZED = "M_UNAUTHORIZED"
|
||||||
|
|
||||||
/** (Not documented yet) */
|
/** (Not documented yet) */
|
||||||
const val M_OLD_VERSION = "M_OLD_VERSION"
|
const val M_OLD_VERSION = "M_OLD_VERSION"
|
||||||
|
|
||||||
/** The server did not understand the request. */
|
/** The server did not understand the request. */
|
||||||
const val M_UNRECOGNIZED = "M_UNRECOGNIZED"
|
const val M_UNRECOGNIZED = "M_UNRECOGNIZED"
|
||||||
|
|
||||||
/** (Not documented yet) */
|
/** (Not documented yet) */
|
||||||
const val M_LOGIN_EMAIL_URL_NOT_YET = "M_LOGIN_EMAIL_URL_NOT_YET"
|
const val M_LOGIN_EMAIL_URL_NOT_YET = "M_LOGIN_EMAIL_URL_NOT_YET"
|
||||||
|
|
||||||
/** Authentication could not be performed on the third party identifier. */
|
/** Authentication could not be performed on the third party identifier. */
|
||||||
const val M_THREEPID_AUTH_FAILED = "M_THREEPID_AUTH_FAILED"
|
const val M_THREEPID_AUTH_FAILED = "M_THREEPID_AUTH_FAILED"
|
||||||
|
|
||||||
/** Sent when a threepid given to an API cannot be used because no record matching the threepid was found. */
|
/** Sent when a threepid given to an API cannot be used because no record matching the threepid was found. */
|
||||||
const val M_THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
|
const val M_THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
|
||||||
|
|
||||||
/** Sent when a threepid given to an API cannot be used because the same threepid is already in use. */
|
/** Sent when a threepid given to an API cannot be used because the same threepid is already in use. */
|
||||||
const val M_THREEPID_IN_USE = "M_THREEPID_IN_USE"
|
const val M_THREEPID_IN_USE = "M_THREEPID_IN_USE"
|
||||||
|
|
||||||
/** The client's request used a third party server, eg. identity server, that this server does not trust. */
|
/** The client's request used a third party server, eg. identity server, that this server does not trust. */
|
||||||
const val M_SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
|
const val M_SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
|
||||||
|
|
||||||
/** The request or entity was too large. */
|
/** The request or entity was too large. */
|
||||||
const val M_TOO_LARGE = "M_TOO_LARGE"
|
const val M_TOO_LARGE = "M_TOO_LARGE"
|
||||||
|
|
||||||
/** (Not documented yet) */
|
/** (Not documented yet) */
|
||||||
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
||||||
|
|
||||||
/** The request cannot be completed because the homeserver has reached a resource limit imposed on it. For example,
|
/** The request cannot be completed because the homeserver has reached a resource limit imposed on it. For example,
|
||||||
* a homeserver held in a shared hosting environment may reach a resource limit if it starts using too much memory
|
* a homeserver held in a shared hosting environment may reach a resource limit if it starts using too much memory
|
||||||
* or disk space. The error MUST have an admin_contact field to provide the user receiving the error a place to reach
|
* or disk space. The error MUST have an admin_contact field to provide the user receiving the error a place to reach
|
||||||
* out to. Typically, this error will appear on routes which attempt to modify state (eg: sending messages, account
|
* out to. Typically, this error will appear on routes which attempt to modify state (eg: sending messages, account
|
||||||
* data, etc) and not routes which only read state (eg: /sync, get account data, etc). */
|
* data, etc) and not routes which only read state (eg: /sync, get account data, etc). */
|
||||||
const val M_RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
|
const val M_RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
|
||||||
|
|
||||||
/** The user ID associated with the request has been deactivated. Typically for endpoints that prove authentication, such as /login. */
|
/** The user ID associated with the request has been deactivated. Typically for endpoints that prove authentication, such as /login. */
|
||||||
const val M_USER_DEACTIVATED = "M_USER_DEACTIVATED"
|
const val M_USER_DEACTIVATED = "M_USER_DEACTIVATED"
|
||||||
|
|
||||||
/** Encountered when trying to register a user ID which is not valid. */
|
/** Encountered when trying to register a user ID which is not valid. */
|
||||||
const val M_INVALID_USERNAME = "M_INVALID_USERNAME"
|
const val M_INVALID_USERNAME = "M_INVALID_USERNAME"
|
||||||
|
|
||||||
/** Sent when the initial state given to the createRoom API is invalid. */
|
/** Sent when the initial state given to the createRoom API is invalid. */
|
||||||
const val M_INVALID_ROOM_STATE = "M_INVALID_ROOM_STATE"
|
const val M_INVALID_ROOM_STATE = "M_INVALID_ROOM_STATE"
|
||||||
|
|
||||||
/** The server does not permit this third party identifier. This may happen if the server only permits,
|
/** The server does not permit this third party identifier. This may happen if the server only permits,
|
||||||
* for example, email addresses from a particular domain. */
|
* for example, email addresses from a particular domain. */
|
||||||
const val M_THREEPID_DENIED = "M_THREEPID_DENIED"
|
const val M_THREEPID_DENIED = "M_THREEPID_DENIED"
|
||||||
|
|
||||||
/** The client's request to create a room used a room version that the server does not support. */
|
/** The client's request to create a room used a room version that the server does not support. */
|
||||||
const val M_UNSUPPORTED_ROOM_VERSION = "M_UNSUPPORTED_ROOM_VERSION"
|
const val M_UNSUPPORTED_ROOM_VERSION = "M_UNSUPPORTED_ROOM_VERSION"
|
||||||
|
|
||||||
/** The client attempted to join a room that has a version the server does not support.
|
/** The client attempted to join a room that has a version the server does not support.
|
||||||
* Inspect the room_version property of the error response for the room's version. */
|
* Inspect the room_version property of the error response for the room's version. */
|
||||||
const val M_INCOMPATIBLE_ROOM_VERSION = "M_INCOMPATIBLE_ROOM_VERSION"
|
const val M_INCOMPATIBLE_ROOM_VERSION = "M_INCOMPATIBLE_ROOM_VERSION"
|
||||||
|
|
||||||
/** The state change requested cannot be performed, such as attempting to unban a user who is not banned. */
|
/** The state change requested cannot be performed, such as attempting to unban a user who is not banned. */
|
||||||
const val M_BAD_STATE = "M_BAD_STATE"
|
const val M_BAD_STATE = "M_BAD_STATE"
|
||||||
|
|
||||||
/** The room or resource does not permit guests to access it. */
|
/** The room or resource does not permit guests to access it. */
|
||||||
const val M_GUEST_ACCESS_FORBIDDEN = "M_GUEST_ACCESS_FORBIDDEN"
|
const val M_GUEST_ACCESS_FORBIDDEN = "M_GUEST_ACCESS_FORBIDDEN"
|
||||||
|
|
||||||
/** A Captcha is required to complete the request. */
|
/** A Captcha is required to complete the request. */
|
||||||
const val M_CAPTCHA_NEEDED = "M_CAPTCHA_NEEDED"
|
const val M_CAPTCHA_NEEDED = "M_CAPTCHA_NEEDED"
|
||||||
|
|
||||||
/** The Captcha provided did not match what was expected. */
|
/** The Captcha provided did not match what was expected. */
|
||||||
const val M_CAPTCHA_INVALID = "M_CAPTCHA_INVALID"
|
const val M_CAPTCHA_INVALID = "M_CAPTCHA_INVALID"
|
||||||
|
|
||||||
/** A required parameter was missing from the request. */
|
/** A required parameter was missing from the request. */
|
||||||
const val M_MISSING_PARAM = "M_MISSING_PARAM"
|
const val M_MISSING_PARAM = "M_MISSING_PARAM"
|
||||||
|
|
||||||
/** A parameter that was specified has the wrong value. For example, the server expected an integer and instead received a string. */
|
/** A parameter that was specified has the wrong value. For example, the server expected an integer and instead received a string. */
|
||||||
const val M_INVALID_PARAM = "M_INVALID_PARAM"
|
const val M_INVALID_PARAM = "M_INVALID_PARAM"
|
||||||
|
|
||||||
/** The resource being requested is reserved by an application service, or the application service making the request has not created the resource. */
|
/** The resource being requested is reserved by an application service, or the application service making the request has not created the resource. */
|
||||||
const val M_EXCLUSIVE = "M_EXCLUSIVE"
|
const val M_EXCLUSIVE = "M_EXCLUSIVE"
|
||||||
|
|
||||||
/** The user is unable to reject an invite to join the server notices room. See the Server Notices module for more information. */
|
/** The user is unable to reject an invite to join the server notices room. See the Server Notices module for more information. */
|
||||||
const val M_CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
|
const val M_CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
|
||||||
|
|
||||||
/** (Not documented yet) */
|
/** (Not documented yet) */
|
||||||
const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
|
const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
|
||||||
|
|
||||||
/** (Not documented yet) */
|
/** (Not documented yet) */
|
||||||
const val M_WEAK_PASSWORD = "M_WEAK_PASSWORD"
|
const val M_WEAK_PASSWORD = "M_WEAK_PASSWORD"
|
||||||
|
|
||||||
|
@ -71,15 +71,15 @@ sealed class Action {
|
|||||||
fun List<Action>.toJson(): List<Any> {
|
fun List<Action>.toJson(): List<Any> {
|
||||||
return map { action ->
|
return map { action ->
|
||||||
when (action) {
|
when (action) {
|
||||||
is Action.Notify -> Action.ACTION_NOTIFY
|
is Action.Notify -> Action.ACTION_NOTIFY
|
||||||
is Action.DoNotNotify -> Action.ACTION_DONT_NOTIFY
|
is Action.DoNotNotify -> Action.ACTION_DONT_NOTIFY
|
||||||
is Action.Sound -> {
|
is Action.Sound -> {
|
||||||
mapOf(
|
mapOf(
|
||||||
Action.ACTION_OBJECT_SET_TWEAK_KEY to Action.ACTION_OBJECT_SET_TWEAK_VALUE_SOUND,
|
Action.ACTION_OBJECT_SET_TWEAK_KEY to Action.ACTION_OBJECT_SET_TWEAK_VALUE_SOUND,
|
||||||
Action.ACTION_OBJECT_VALUE_KEY to action.sound
|
Action.ACTION_OBJECT_VALUE_KEY to action.sound
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Action.Highlight -> {
|
is Action.Highlight -> {
|
||||||
mapOf(
|
mapOf(
|
||||||
Action.ACTION_OBJECT_SET_TWEAK_KEY to Action.ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT,
|
Action.ACTION_OBJECT_SET_TWEAK_KEY to Action.ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT,
|
||||||
Action.ACTION_OBJECT_VALUE_KEY to action.highlight
|
Action.ACTION_OBJECT_VALUE_KEY to action.highlight
|
||||||
@ -94,9 +94,9 @@ fun PushRule.getActions(): List<Action> {
|
|||||||
|
|
||||||
actions.forEach { actionStrOrObj ->
|
actions.forEach { actionStrOrObj ->
|
||||||
when (actionStrOrObj) {
|
when (actionStrOrObj) {
|
||||||
Action.ACTION_NOTIFY -> Action.Notify
|
Action.ACTION_NOTIFY -> Action.Notify
|
||||||
Action.ACTION_DONT_NOTIFY -> Action.DoNotNotify
|
Action.ACTION_DONT_NOTIFY -> Action.DoNotNotify
|
||||||
is Map<*, *> -> {
|
is Map<*, *> -> {
|
||||||
when (actionStrOrObj[Action.ACTION_OBJECT_SET_TWEAK_KEY]) {
|
when (actionStrOrObj[Action.ACTION_OBJECT_SET_TWEAK_KEY]) {
|
||||||
Action.ACTION_OBJECT_SET_TWEAK_VALUE_SOUND -> {
|
Action.ACTION_OBJECT_SET_TWEAK_VALUE_SOUND -> {
|
||||||
(actionStrOrObj[Action.ACTION_OBJECT_VALUE_KEY] as? String)?.let { stringValue ->
|
(actionStrOrObj[Action.ACTION_OBJECT_VALUE_KEY] as? String)?.let { stringValue ->
|
||||||
@ -112,13 +112,13 @@ fun PushRule.getActions(): List<Action> {
|
|||||||
// When the value is not there, default is true, says the spec
|
// When the value is not there, default is true, says the spec
|
||||||
?: Action.Highlight(true)
|
?: Action.Highlight(true)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Timber.w("Unsupported set_tweak value ${actionStrOrObj[Action.ACTION_OBJECT_SET_TWEAK_KEY]}")
|
Timber.w("Unsupported set_tweak value ${actionStrOrObj[Action.ACTION_OBJECT_SET_TWEAK_KEY]}")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Timber.w("Unsupported action type $actionStrOrObj")
|
Timber.w("Unsupported action type $actionStrOrObj")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,12 @@ package org.matrix.android.sdk.api.raw
|
|||||||
|
|
||||||
sealed class RawCacheStrategy {
|
sealed class RawCacheStrategy {
|
||||||
// Data is always fetched from the server
|
// Data is always fetched from the server
|
||||||
object NoCache: RawCacheStrategy()
|
object NoCache : RawCacheStrategy()
|
||||||
|
|
||||||
// Once data is retrieved, it is stored for the provided amount of time.
|
// Once data is retrieved, it is stored for the provided amount of time.
|
||||||
// In case of error, and if strict is set to false, the cache can be returned if available
|
// In case of error, and if strict is set to false, the cache can be returned if available
|
||||||
data class TtlCache(val validityDurationInMillis: Long, val strict: Boolean): RawCacheStrategy()
|
data class TtlCache(val validityDurationInMillis: Long, val strict: Boolean) : RawCacheStrategy()
|
||||||
|
|
||||||
// Once retrieved, the data is stored in cache and will be always get from the cache
|
// Once retrieved, the data is stored in cache and will be always get from the cache
|
||||||
object InfiniteCache: RawCacheStrategy()
|
object InfiniteCache : RawCacheStrategy()
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ interface CallSignalingService {
|
|||||||
|
|
||||||
fun removeCallListener(listener: CallsListener)
|
fun removeCallListener(listener: CallsListener)
|
||||||
|
|
||||||
fun getCallWithId(callId: String) : MxCall?
|
fun getCallWithId(callId: String): MxCall?
|
||||||
|
|
||||||
fun isThereAnyActiveCall(): Boolean
|
fun isThereAnyActiveCall(): Boolean
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ interface MxCallDetail {
|
|||||||
interface MxCall : MxCallDetail {
|
interface MxCall : MxCallDetail {
|
||||||
|
|
||||||
var state: CallState
|
var state: CallState
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pick Up the incoming call
|
* Pick Up the incoming call
|
||||||
* It has no effect on outgoing call
|
* It has no effect on outgoing call
|
||||||
|
@ -101,9 +101,9 @@ interface CryptoService {
|
|||||||
|
|
||||||
fun fetchDevicesList(callback: MatrixCallback<DevicesListResponse>)
|
fun fetchDevicesList(callback: MatrixCallback<DevicesListResponse>)
|
||||||
|
|
||||||
fun getMyDevicesInfo() : List<DeviceInfo>
|
fun getMyDevicesInfo(): List<DeviceInfo>
|
||||||
|
|
||||||
fun getLiveMyDevicesInfo() : LiveData<List<DeviceInfo>>
|
fun getLiveMyDevicesInfo(): LiveData<List<DeviceInfo>>
|
||||||
|
|
||||||
fun getDeviceInfo(deviceId: String, callback: MatrixCallback<DeviceInfo>)
|
fun getDeviceInfo(deviceId: String, callback: MatrixCallback<DeviceInfo>)
|
||||||
|
|
||||||
@ -148,6 +148,6 @@ interface CryptoService {
|
|||||||
fun getGossipingEventsTrail(): List<Event>
|
fun getGossipingEventsTrail(): List<Event>
|
||||||
|
|
||||||
// For testing shared session
|
// For testing shared session
|
||||||
fun getSharedWithInfo(roomId: String?, sessionId: String) : MXUsersDevicesMap<Int>
|
fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int>
|
||||||
fun getWithHeldMegolmSession(roomId: String, sessionId: String) : RoomKeyWithHeldContent?
|
fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent?
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ interface KeysBackupService {
|
|||||||
|
|
||||||
// For gossiping
|
// For gossiping
|
||||||
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
||||||
fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo?
|
fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo?
|
||||||
|
|
||||||
fun isValidRecoveryKeyForCurrentVersion(recoveryKey: String, callback: MatrixCallback<Boolean>)
|
fun isValidRecoveryKeyForCurrentVersion(recoveryKey: String, callback: MatrixCallback<Boolean>)
|
||||||
}
|
}
|
||||||
|
@ -53,23 +53,31 @@ package org.matrix.android.sdk.api.session.crypto.keysbackup
|
|||||||
enum class KeysBackupState {
|
enum class KeysBackupState {
|
||||||
// Need to check the current backup version on the homeserver
|
// Need to check the current backup version on the homeserver
|
||||||
Unknown,
|
Unknown,
|
||||||
|
|
||||||
// Checking if backup is enabled on home server
|
// Checking if backup is enabled on home server
|
||||||
CheckingBackUpOnHomeserver,
|
CheckingBackUpOnHomeserver,
|
||||||
|
|
||||||
// Backup has been stopped because a new backup version has been detected on the homeserver
|
// Backup has been stopped because a new backup version has been detected on the homeserver
|
||||||
WrongBackUpVersion,
|
WrongBackUpVersion,
|
||||||
|
|
||||||
// Backup from this device is not enabled
|
// Backup from this device is not enabled
|
||||||
Disabled,
|
Disabled,
|
||||||
|
|
||||||
// There is a backup available on the homeserver but it is not trusted.
|
// There is a backup available on the homeserver but it is not trusted.
|
||||||
// It is not trusted because the signature is invalid or the device that created it is not verified
|
// It is not trusted because the signature is invalid or the device that created it is not verified
|
||||||
// Use [KeysBackup.getKeysBackupTrust()] to get trust details.
|
// Use [KeysBackup.getKeysBackupTrust()] to get trust details.
|
||||||
// Consequently, the backup from this device is not enabled.
|
// Consequently, the backup from this device is not enabled.
|
||||||
NotTrusted,
|
NotTrusted,
|
||||||
|
|
||||||
// Backup is being enabled: the backup version is being created on the homeserver
|
// Backup is being enabled: the backup version is being created on the homeserver
|
||||||
Enabling,
|
Enabling,
|
||||||
|
|
||||||
// Backup is enabled and ready to send backup to the homeserver
|
// Backup is enabled and ready to send backup to the homeserver
|
||||||
ReadyToBackUp,
|
ReadyToBackUp,
|
||||||
|
|
||||||
// e2e keys are going to be sent to the homeserver
|
// e2e keys are going to be sent to the homeserver
|
||||||
WillBackUp,
|
WillBackUp,
|
||||||
|
|
||||||
// e2e keys are being sent to the homeserver
|
// e2e keys are being sent to the homeserver
|
||||||
BackingUp
|
BackingUp
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ interface GossipingRequestListener {
|
|||||||
* Returns the secret value to be shared
|
* Returns the secret value to be shared
|
||||||
* @return true if is handled
|
* @return true if is handled
|
||||||
*/
|
*/
|
||||||
fun onSecretShareRequest(request: IncomingSecretShareRequest) : Boolean
|
fun onSecretShareRequest(request: IncomingSecretShareRequest): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A room key request cancellation has been received.
|
* A room key request cancellation has been received.
|
||||||
|
@ -22,8 +22,10 @@ package org.matrix.android.sdk.api.session.crypto.verification
|
|||||||
enum class VerificationMethod {
|
enum class VerificationMethod {
|
||||||
// Use it when your application supports the SAS verification method
|
// Use it when your application supports the SAS verification method
|
||||||
SAS,
|
SAS,
|
||||||
|
|
||||||
// Use it if your application is able to display QR codes
|
// Use it if your application is able to display QR codes
|
||||||
QR_CODE_SHOW,
|
QR_CODE_SHOW,
|
||||||
|
|
||||||
// Use it if your application is able to scan QR codes
|
// Use it if your application is able to scan QR codes
|
||||||
QR_CODE_SCAN
|
QR_CODE_SCAN
|
||||||
}
|
}
|
||||||
|
@ -253,6 +253,7 @@ fun Event.isFileMessage(): Boolean {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isAttachmentMessage(): Boolean {
|
fun Event.isAttachmentMessage(): Boolean {
|
||||||
return getClearType() == EventType.MESSAGE
|
return getClearType() == EventType.MESSAGE
|
||||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||||
|
@ -21,10 +21,13 @@ package org.matrix.android.sdk.api.session.events.model
|
|||||||
object RelationType {
|
object RelationType {
|
||||||
/** Lets you define an event which annotates an existing event.*/
|
/** Lets you define an event which annotates an existing event.*/
|
||||||
const val ANNOTATION = "m.annotation"
|
const val ANNOTATION = "m.annotation"
|
||||||
|
|
||||||
/** Lets you define an event which replaces an existing event.*/
|
/** Lets you define an event which replaces an existing event.*/
|
||||||
const val REPLACE = "m.replace"
|
const val REPLACE = "m.replace"
|
||||||
|
|
||||||
/** Lets you define an event which references an existing event.*/
|
/** Lets you define an event which references an existing event.*/
|
||||||
const val REFERENCE = "m.reference"
|
const val REFERENCE = "m.reference"
|
||||||
|
|
||||||
/** Lets you define an event which adds a response to an existing event.*/
|
/** Lets you define an event which adds a response to an existing event.*/
|
||||||
const val RESPONSE = "org.matrix.response"
|
const val RESPONSE = "org.matrix.response"
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,6 @@
|
|||||||
package org.matrix.android.sdk.api.session.events.model
|
package org.matrix.android.sdk.api.session.events.model
|
||||||
|
|
||||||
interface UnsignedRelationInfo {
|
interface UnsignedRelationInfo {
|
||||||
val limited : Boolean?
|
val limited: Boolean?
|
||||||
val count: Int?
|
val count: Int?
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,12 @@ data class IntegrationManagerConfig(
|
|||||||
* Defined in UserAccountData
|
* Defined in UserAccountData
|
||||||
*/
|
*/
|
||||||
ACCOUNT,
|
ACCOUNT,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defined in Wellknown
|
* Defined in Wellknown
|
||||||
*/
|
*/
|
||||||
HOMESERVER,
|
HOMESERVER,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fallback value, hardcoded by the SDK
|
* Fallback value, hardcoded by the SDK
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.members
|
package org.matrix.android.sdk.api.session.room.members
|
||||||
|
|
||||||
sealed class ChangeMembershipState() {
|
sealed class ChangeMembershipState {
|
||||||
object Unknown : ChangeMembershipState()
|
object Unknown : ChangeMembershipState()
|
||||||
object Joining : ChangeMembershipState()
|
object Joining : ChangeMembershipState()
|
||||||
data class FailedJoining(val throwable: Throwable) : ChangeMembershipState()
|
data class FailedJoining(val throwable: Throwable) : ChangeMembershipState()
|
||||||
|
@ -35,7 +35,7 @@ data class PollSummaryContent(
|
|||||||
return votes?.size ?: 0
|
return votes?.size ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun voteCountForOption(optionIndex: Int) : Int {
|
fun voteCountForOption(optionIndex: Int): Int {
|
||||||
return votes?.filter { it.optionIndex == optionIndex }?.count() ?: 0
|
return votes?.filter { it.optionIndex == optionIndex }?.count() ?: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ data class RoomGuestAccessContent(
|
|||||||
enum class GuestAccess(val value: String) {
|
enum class GuestAccess(val value: String) {
|
||||||
@Json(name = "can_join")
|
@Json(name = "can_join")
|
||||||
CanJoin("can_join"),
|
CanJoin("can_join"),
|
||||||
|
|
||||||
@Json(name = "forbidden")
|
@Json(name = "forbidden")
|
||||||
Forbidden("forbidden")
|
Forbidden("forbidden")
|
||||||
}
|
}
|
||||||
|
@ -29,16 +29,19 @@ enum class RoomHistoryVisibility {
|
|||||||
* participating homeserver with anyone, regardless of whether they have ever joined the room.
|
* participating homeserver with anyone, regardless of whether they have ever joined the room.
|
||||||
*/
|
*/
|
||||||
@Json(name = "world_readable") WORLD_READABLE,
|
@Json(name = "world_readable") WORLD_READABLE,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Previous events are always accessible to newly joined members. All events in the
|
* Previous events are always accessible to newly joined members. All events in the
|
||||||
* room are accessible, even those sent when the member was not a part of the room.
|
* room are accessible, even those sent when the member was not a part of the room.
|
||||||
*/
|
*/
|
||||||
@Json(name = "shared") SHARED,
|
@Json(name = "shared") SHARED,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Events are accessible to newly joined members from the point they were invited onwards.
|
* Events are accessible to newly joined members from the point they were invited onwards.
|
||||||
* Events stop being accessible when the member's state changes to something other than invite or join.
|
* Events stop being accessible when the member's state changes to something other than invite or join.
|
||||||
*/
|
*/
|
||||||
@Json(name = "invited") INVITED,
|
@Json(name = "invited") INVITED,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Events are accessible to newly joined members from the point they joined the room onwards.
|
* Events are accessible to newly joined members from the point they joined the room onwards.
|
||||||
* Events stop being accessible when the member's state changes to something other than join.
|
* Events stop being accessible when the member's state changes to something other than join.
|
||||||
|
@ -53,6 +53,6 @@ data class MessageAudioContent(
|
|||||||
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
||||||
) : MessageWithAttachmentContent {
|
) : MessageWithAttachmentContent {
|
||||||
|
|
||||||
override val mimeType: String?
|
override val mimeType: String?
|
||||||
get() = encryptedFileInfo?.mimetype ?: audioInfo?.mimeType
|
get() = encryptedFileInfo?.mimetype ?: audioInfo?.mimeType
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,6 @@ data class MessageImageContent(
|
|||||||
*/
|
*/
|
||||||
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
||||||
) : MessageImageInfoContent {
|
) : MessageImageInfoContent {
|
||||||
override val mimeType: String?
|
override val mimeType: String?
|
||||||
get() = encryptedFileInfo?.mimetype ?: info?.mimeType ?: "image/*"
|
get() = encryptedFileInfo?.mimetype ?: info?.mimeType ?: "image/*"
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,6 @@ data class MessageStickerContent(
|
|||||||
*/
|
*/
|
||||||
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
||||||
) : MessageImageInfoContent {
|
) : MessageImageInfoContent {
|
||||||
override val mimeType: String?
|
override val mimeType: String?
|
||||||
get() = encryptedFileInfo?.mimetype ?: info?.mimeType
|
get() = encryptedFileInfo?.mimetype ?: info?.mimeType
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ object MessageType {
|
|||||||
const val MSGTYPE_RESPONSE = "org.matrix.response"
|
const val MSGTYPE_RESPONSE = "org.matrix.response"
|
||||||
const val MSGTYPE_POLL_CLOSED = "org.matrix.poll_closed"
|
const val MSGTYPE_POLL_CLOSED = "org.matrix.poll_closed"
|
||||||
const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request"
|
const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request"
|
||||||
|
|
||||||
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
|
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
|
||||||
// Because sticker isn't a message type but a event type without msgtype field
|
// Because sticker isn't a message type but a event type without msgtype field
|
||||||
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
|
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
|
||||||
|
@ -52,6 +52,6 @@ data class MessageVideoContent(
|
|||||||
*/
|
*/
|
||||||
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
|
||||||
) : MessageWithAttachmentContent {
|
) : MessageWithAttachmentContent {
|
||||||
override val mimeType: String?
|
override val mimeType: String?
|
||||||
get() = encryptedFileInfo?.mimetype ?: videoInfo?.mimeType
|
get() = encryptedFileInfo?.mimetype ?: videoInfo?.mimeType
|
||||||
}
|
}
|
||||||
|
@ -18,18 +18,25 @@ package org.matrix.android.sdk.api.session.room.send
|
|||||||
|
|
||||||
enum class SendState {
|
enum class SendState {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
|
|
||||||
// the event has not been sent
|
// the event has not been sent
|
||||||
UNSENT,
|
UNSENT,
|
||||||
|
|
||||||
// the event is encrypting
|
// the event is encrypting
|
||||||
ENCRYPTING,
|
ENCRYPTING,
|
||||||
|
|
||||||
// the event is currently sending
|
// the event is currently sending
|
||||||
SENDING,
|
SENDING,
|
||||||
|
|
||||||
// the event has been sent
|
// the event has been sent
|
||||||
SENT,
|
SENT,
|
||||||
|
|
||||||
// the event has been received from server
|
// the event has been received from server
|
||||||
SYNCED,
|
SYNCED,
|
||||||
|
|
||||||
// The event failed to be sent
|
// The event failed to be sent
|
||||||
UNDELIVERED,
|
UNDELIVERED,
|
||||||
|
|
||||||
// the event failed to be sent because some unknown devices have been found while encrypting it
|
// the event failed to be sent because some unknown devices have been found while encrypting it
|
||||||
FAILED_UNKNOWN_DEVICES;
|
FAILED_UNKNOWN_DEVICES;
|
||||||
|
|
||||||
|
@ -128,6 +128,7 @@ interface Timeline {
|
|||||||
* It represents future events.
|
* It represents future events.
|
||||||
*/
|
*/
|
||||||
FORWARDS,
|
FORWARDS,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It represents past events.
|
* It represents past events.
|
||||||
*/
|
*/
|
||||||
|
@ -40,5 +40,5 @@ interface TimelineService {
|
|||||||
|
|
||||||
fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>>
|
fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>>
|
||||||
|
|
||||||
fun getAttachmentMessages() : List<TimelineEvent>
|
fun getAttachmentMessages(): List<TimelineEvent>
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ interface FilterService {
|
|||||||
|
|
||||||
enum class FilterPreset {
|
enum class FilterPreset {
|
||||||
NoFilter,
|
NoFilter,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter for Riot, will include only known event type
|
* Filter for Riot, will include only known event type
|
||||||
*/
|
*/
|
||||||
|
@ -46,7 +46,7 @@ data class Optional<T : Any> constructor(private val value: T?) {
|
|||||||
return Optional(value)
|
return Optional(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T: Any> empty(): Optional<T> {
|
fun <T : Any> empty(): Optional<T> {
|
||||||
return Optional(null)
|
return Optional(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ const val MXCRYPTO_ALGORITHM_MEGOLM_BACKUP = "m.megolm_backup.v1.curve25519-aes-
|
|||||||
* Secured Shared Storage algorithm constant
|
* Secured Shared Storage algorithm constant
|
||||||
*/
|
*/
|
||||||
const val SSSS_ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
|
const val SSSS_ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
|
||||||
|
|
||||||
/* Secrets are encrypted using AES-CTR-256 and MACed using HMAC-SHA-256. **/
|
/* Secrets are encrypted using AES-CTR-256 and MACed using HMAC-SHA-256. **/
|
||||||
const val SSSS_ALGORITHM_AES_HMAC_SHA2 = "m.secret_storage.v1.aes-hmac-sha2"
|
const val SSSS_ALGORITHM_AES_HMAC_SHA2 = "m.secret_storage.v1.aes-hmac-sha2"
|
||||||
|
|
||||||
|
@ -194,18 +194,18 @@ internal class DefaultCryptoService @Inject constructor(
|
|||||||
private val lastNewSessionForcedDates = MXUsersDevicesMap<Long>()
|
private val lastNewSessionForcedDates = MXUsersDevicesMap<Long>()
|
||||||
|
|
||||||
fun onStateEvent(roomId: String, event: Event) {
|
fun onStateEvent(roomId: String, event: Event) {
|
||||||
when {
|
when (event.getClearType()) {
|
||||||
event.getClearType() == EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
|
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
|
||||||
event.getClearType() == EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
|
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
|
||||||
event.getClearType() == EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
|
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onLiveEvent(roomId: String, event: Event) {
|
fun onLiveEvent(roomId: String, event: Event) {
|
||||||
when {
|
when (event.getClearType()) {
|
||||||
event.getClearType() == EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
|
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
|
||||||
event.getClearType() == EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
|
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
|
||||||
event.getClearType() == EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
|
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,6 +615,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||||||
val encryptionEvent = monarchy.fetchCopied { realm ->
|
val encryptionEvent = monarchy.fetchCopied { realm ->
|
||||||
EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
|
EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
|
||||||
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
|
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
|
||||||
|
.isNotNull(EventEntityFields.STATE_KEY)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
}
|
}
|
||||||
return encryptionEvent != null
|
return encryptionEvent != null
|
||||||
@ -915,6 +916,11 @@ internal class DefaultCryptoService @Inject constructor(
|
|||||||
* @param event the encryption event.
|
* @param event the encryption event.
|
||||||
*/
|
*/
|
||||||
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
|
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
|
||||||
|
if (!event.isStateEvent()) {
|
||||||
|
// Ignore
|
||||||
|
Timber.w("Invalid encryption event")
|
||||||
|
return
|
||||||
|
}
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||||
val params = LoadRoomMembersTask.Params(roomId)
|
val params = LoadRoomMembersTask.Params(roomId)
|
||||||
try {
|
try {
|
||||||
|
@ -359,7 +359,6 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||||||
cryptoStore.storeUserDevices(userId, workingCopy)
|
cryptoStore.storeUserDevices(userId, workingCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle cross signing keys update
|
|
||||||
val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also {
|
val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also {
|
||||||
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}")
|
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}")
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,8 @@ enum class GossipingRequestState {
|
|||||||
ACCEPTING,
|
ACCEPTING,
|
||||||
ACCEPTED,
|
ACCEPTED,
|
||||||
FAILED_TO_ACCEPTED,
|
FAILED_TO_ACCEPTED,
|
||||||
// USER_REJECTED,
|
|
||||||
|
// USER_REJECTED,
|
||||||
UNABLE_TO_PROCESS,
|
UNABLE_TO_PROCESS,
|
||||||
CANCELLED_BY_REQUESTER,
|
CANCELLED_BY_REQUESTER,
|
||||||
RE_REQUESTED
|
RE_REQUESTED
|
||||||
|
@ -36,6 +36,7 @@ import kotlin.math.min
|
|||||||
object MXMegolmExportEncryption {
|
object MXMegolmExportEncryption {
|
||||||
private const val HEADER_LINE = "-----BEGIN MEGOLM SESSION DATA-----"
|
private const val HEADER_LINE = "-----BEGIN MEGOLM SESSION DATA-----"
|
||||||
private const val TRAILER_LINE = "-----END MEGOLM SESSION DATA-----"
|
private const val TRAILER_LINE = "-----END MEGOLM SESSION DATA-----"
|
||||||
|
|
||||||
// we split into lines before base64ing, because encodeBase64 doesn't deal
|
// we split into lines before base64ing, because encodeBase64 doesn't deal
|
||||||
// terribly well with large arrays.
|
// terribly well with large arrays.
|
||||||
private const val LINE_LENGTH = 72 * 4 / 3
|
private const val LINE_LENGTH = 72 * 4 / 3
|
||||||
|
@ -70,7 +70,9 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
|||||||
delay(1500)
|
delay(1500)
|
||||||
cryptoStore.getOrAddOutgoingSecretShareRequest(secretName, recipients)?.let {
|
cryptoStore.getOrAddOutgoingSecretShareRequest(secretName, recipients)?.let {
|
||||||
// TODO check if there is already one that is being sent?
|
// TODO check if there is already one that is being sent?
|
||||||
if (it.state == OutgoingGossipingRequestState.SENDING /**|| it.state == OutgoingGossipingRequestState.SENT*/) {
|
if (it.state == OutgoingGossipingRequestState.SENDING
|
||||||
|
/**|| it.state == OutgoingGossipingRequestState.SENT*/
|
||||||
|
) {
|
||||||
Timber.v("## CRYPTO - GOSSIP sendSecretShareRequest() : we are already sending for that session: $it")
|
Timber.v("## CRYPTO - GOSSIP sendSecretShareRequest() : we are already sending for that session: $it")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ internal interface IMXDecrypting {
|
|||||||
*/
|
*/
|
||||||
fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {}
|
fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {}
|
||||||
|
|
||||||
fun shareSecretWithDevice(request: IncomingSecretShareRequest, secretValue : String) {}
|
fun shareSecretWithDevice(request: IncomingSecretShareRequest, secretValue: String) {}
|
||||||
|
|
||||||
fun requestKeysForEvent(event: Event, withHeld: Boolean)
|
fun requestKeysForEvent(event: Event, withHeld: Boolean)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||||||
senderCurve25519Key = olmDecryptionResult.senderKey,
|
senderCurve25519Key = olmDecryptionResult.senderKey,
|
||||||
claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"),
|
claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"),
|
||||||
forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain
|
forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||||
|
@ -43,7 +43,7 @@ internal class ShieldTrustUpdater @Inject constructor(
|
|||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
@SessionDatabase private val sessionRealmConfiguration: RealmConfiguration,
|
@SessionDatabase private val sessionRealmConfiguration: RealmConfiguration,
|
||||||
private val roomSummaryUpdater: RoomSummaryUpdater
|
private val roomSummaryUpdater: RoomSummaryUpdater
|
||||||
): SessionLifecycleObserver {
|
) : SessionLifecycleObserver {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val BACKGROUND_HANDLER = createBackgroundHandler("SHIELD_CRYPTO_DB_THREAD")
|
private val BACKGROUND_HANDLER = createBackgroundHandler("SHIELD_CRYPTO_DB_THREAD")
|
||||||
|
@ -80,6 +80,7 @@ class OlmInboundGroupSessionWrapper2 : Serializable {
|
|||||||
constructor() {
|
constructor() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance from the provided keys map.
|
* Create a new instance from the provided keys map.
|
||||||
*
|
*
|
||||||
|
@ -66,19 +66,23 @@ enum class WithHeldCode(val value: String) {
|
|||||||
* the user/device was blacklisted
|
* the user/device was blacklisted
|
||||||
*/
|
*/
|
||||||
BLACKLISTED("m.blacklisted"),
|
BLACKLISTED("m.blacklisted"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the user/devices is unverified
|
* the user/devices is unverified
|
||||||
*/
|
*/
|
||||||
UNVERIFIED("m.unverified"),
|
UNVERIFIED("m.unverified"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the user/device is not allowed have the key. For example, this would usually be sent in response
|
* the user/device is not allowed have the key. For example, this would usually be sent in response
|
||||||
* to a key request if the user was not in the room when the message was sent
|
* to a key request if the user was not in the room when the message was sent
|
||||||
*/
|
*/
|
||||||
UNAUTHORISED("m.unauthorised"),
|
UNAUTHORISED("m.unauthorised"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent in reply to a key request if the device that the key is requested from does not have the requested key
|
* Sent in reply to a key request if the device that the key is requested from does not have the requested key
|
||||||
*/
|
*/
|
||||||
UNAVAILABLE("m.unavailable"),
|
UNAVAILABLE("m.unavailable"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An olm session could not be established.
|
* An olm session could not be established.
|
||||||
* This may happen, for example, if the sender was unable to obtain a one-time key from the recipient.
|
* This may happen, for example, if the sender was unable to obtain a one-time key from the recipient.
|
||||||
|
@ -17,6 +17,6 @@
|
|||||||
package org.matrix.android.sdk.internal.crypto.store
|
package org.matrix.android.sdk.internal.crypto.store
|
||||||
|
|
||||||
data class SavedKeyBackupKeyInfo(
|
data class SavedKeyBackupKeyInfo(
|
||||||
val recoveryKey : String,
|
val recoveryKey: String,
|
||||||
val version: String
|
val version: String
|
||||||
)
|
)
|
||||||
|
@ -23,7 +23,6 @@ import io.realm.Realm
|
|||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import io.realm.Sort
|
import io.realm.Sort
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
||||||
@ -86,7 +85,9 @@ import org.matrix.android.sdk.internal.crypto.store.db.query.getById
|
|||||||
import org.matrix.android.sdk.internal.crypto.store.db.query.getOrCreate
|
import org.matrix.android.sdk.internal.crypto.store.db.query.getOrCreate
|
||||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||||
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
||||||
|
import org.matrix.android.sdk.internal.di.DeviceId
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
import org.matrix.olm.OlmException
|
import org.matrix.olm.OlmException
|
||||||
@ -98,7 +99,9 @@ import kotlin.collections.set
|
|||||||
internal class RealmCryptoStore @Inject constructor(
|
internal class RealmCryptoStore @Inject constructor(
|
||||||
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
||||||
private val crossSigningKeysMapper: CrossSigningKeysMapper,
|
private val crossSigningKeysMapper: CrossSigningKeysMapper,
|
||||||
private val credentials: Credentials) : IMXCryptoStore {
|
@UserId private val userId: String,
|
||||||
|
@DeviceId private val deviceId: String?
|
||||||
|
) : IMXCryptoStore {
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* Memory cache, to correctly release JNI objects
|
* Memory cache, to correctly release JNI objects
|
||||||
@ -141,8 +144,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 != credentials.userId
|
if (currentMetadata.userId != userId
|
||||||
|| (credentials.deviceId != null && credentials.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
|
||||||
@ -155,8 +158,8 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Metadata not found, or database cleaned, create it
|
// Metadata not found, or database cleaned, create it
|
||||||
realm.createObject(CryptoMetadataEntity::class.java, credentials.userId).apply {
|
realm.createObject(CryptoMetadataEntity::class.java, userId).apply {
|
||||||
deviceId = credentials.deviceId
|
deviceId = this@RealmCryptoStore.deviceId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,22 +305,42 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
userEntity.crossSigningInfoEntity?.deleteFromRealm()
|
userEntity.crossSigningInfoEntity?.deleteFromRealm()
|
||||||
userEntity.crossSigningInfoEntity = null
|
userEntity.crossSigningInfoEntity = null
|
||||||
} else {
|
} else {
|
||||||
|
var shouldResetMyDevicesLocalTrust = false
|
||||||
CrossSigningInfoEntity.getOrCreate(realm, userId).let { signingInfo ->
|
CrossSigningInfoEntity.getOrCreate(realm, userId).let { signingInfo ->
|
||||||
// What should we do if we detect a change of the keys?
|
// What should we do if we detect a change of the keys?
|
||||||
val existingMaster = signingInfo.getMasterKey()
|
val existingMaster = signingInfo.getMasterKey()
|
||||||
if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) {
|
if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) {
|
||||||
crossSigningKeysMapper.update(existingMaster, masterKey)
|
crossSigningKeysMapper.update(existingMaster, masterKey)
|
||||||
} else {
|
} else {
|
||||||
|
Timber.d("## CrossSigning MSK change for $userId")
|
||||||
val keyEntity = crossSigningKeysMapper.map(masterKey)
|
val keyEntity = crossSigningKeysMapper.map(masterKey)
|
||||||
signingInfo.setMasterKey(keyEntity)
|
signingInfo.setMasterKey(keyEntity)
|
||||||
|
if (userId == this.userId) {
|
||||||
|
shouldResetMyDevicesLocalTrust = true
|
||||||
|
// my msk has changed! clear my private key
|
||||||
|
// Could we have some race here? e.g I am the one that did change the keys
|
||||||
|
// could i get this update to early and clear the private keys?
|
||||||
|
// -> initializeCrossSigning is guarding for that by storing all at once
|
||||||
|
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
|
||||||
|
xSignMasterPrivateKey = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val existingSelfSigned = signingInfo.getSelfSignedKey()
|
val existingSelfSigned = signingInfo.getSelfSignedKey()
|
||||||
if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) {
|
if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) {
|
||||||
crossSigningKeysMapper.update(existingSelfSigned, selfSigningKey)
|
crossSigningKeysMapper.update(existingSelfSigned, selfSigningKey)
|
||||||
} else {
|
} else {
|
||||||
|
Timber.d("## CrossSigning SSK change for $userId")
|
||||||
val keyEntity = crossSigningKeysMapper.map(selfSigningKey)
|
val keyEntity = crossSigningKeysMapper.map(selfSigningKey)
|
||||||
signingInfo.setSelfSignedKey(keyEntity)
|
signingInfo.setSelfSignedKey(keyEntity)
|
||||||
|
if (userId == this.userId) {
|
||||||
|
shouldResetMyDevicesLocalTrust = true
|
||||||
|
// my ssk has changed! clear my private key
|
||||||
|
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
|
||||||
|
xSignSelfSignedPrivateKey = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only for me
|
// Only for me
|
||||||
@ -326,10 +349,29 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
if (existingUSK != null && existingUSK.publicKeyBase64 == userSigningKey.unpaddedBase64PublicKey) {
|
if (existingUSK != null && existingUSK.publicKeyBase64 == userSigningKey.unpaddedBase64PublicKey) {
|
||||||
crossSigningKeysMapper.update(existingUSK, userSigningKey)
|
crossSigningKeysMapper.update(existingUSK, userSigningKey)
|
||||||
} else {
|
} else {
|
||||||
|
Timber.d("## CrossSigning USK change for $userId")
|
||||||
val keyEntity = crossSigningKeysMapper.map(userSigningKey)
|
val keyEntity = crossSigningKeysMapper.map(userSigningKey)
|
||||||
signingInfo.setUserSignedKey(keyEntity)
|
signingInfo.setUserSignedKey(keyEntity)
|
||||||
|
if (userId == this.userId) {
|
||||||
|
shouldResetMyDevicesLocalTrust = true
|
||||||
|
// my usk has changed! clear my private key
|
||||||
|
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
|
||||||
|
xSignUserPrivateKey = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When my cross signing keys are reset, we consider clearing all existing device trust
|
||||||
|
if (shouldResetMyDevicesLocalTrust) {
|
||||||
|
realm.where<UserEntity>()
|
||||||
|
.equalTo(UserEntityFields.USER_ID, this.userId)
|
||||||
|
.findFirst()
|
||||||
|
?.devices?.forEach {
|
||||||
|
it?.trustLevelEntity?.crossSignedVerified = false
|
||||||
|
it?.trustLevelEntity?.locallyVerified = it.deviceId == deviceId
|
||||||
|
}
|
||||||
|
}
|
||||||
userEntity.crossSigningInfoEntity = signingInfo
|
userEntity.crossSigningInfoEntity = signingInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1197,7 +1239,7 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
.findAll()
|
.findAll()
|
||||||
.mapNotNull { entity ->
|
.mapNotNull { entity ->
|
||||||
when (entity.type) {
|
when (entity.type) {
|
||||||
GossipRequestType.KEY -> {
|
GossipRequestType.KEY -> {
|
||||||
IncomingRoomKeyRequest(
|
IncomingRoomKeyRequest(
|
||||||
userId = entity.otherUserId,
|
userId = entity.otherUserId,
|
||||||
deviceId = entity.otherDeviceId,
|
deviceId = entity.otherDeviceId,
|
||||||
@ -1316,7 +1358,7 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
.findAll()
|
.findAll()
|
||||||
xInfoEntities?.forEach { info ->
|
xInfoEntities?.forEach { info ->
|
||||||
// Need to ignore mine
|
// Need to ignore mine
|
||||||
if (info.userId != credentials.userId) {
|
if (info.userId != userId) {
|
||||||
info.crossSigningKeys.forEach {
|
info.crossSigningKeys.forEach {
|
||||||
it.trustLevelEntity = null
|
it.trustLevelEntity = null
|
||||||
}
|
}
|
||||||
@ -1331,7 +1373,7 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
.findAll()
|
.findAll()
|
||||||
xInfoEntities?.forEach { xInfoEntity ->
|
xInfoEntities?.forEach { xInfoEntity ->
|
||||||
// Need to ignore mine
|
// Need to ignore mine
|
||||||
if (xInfoEntity.userId == credentials.userId) return@forEach
|
if (xInfoEntity.userId == userId) return@forEach
|
||||||
val mapped = mapCrossSigningInfoEntity(xInfoEntity)
|
val mapped = mapCrossSigningInfoEntity(xInfoEntity)
|
||||||
val currentTrust = mapped.isTrusted()
|
val currentTrust = mapped.isTrusted()
|
||||||
val newTrust = check(mapped.userId)
|
val newTrust = check(mapped.userId)
|
||||||
|
@ -26,10 +26,10 @@ internal fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, devi
|
|||||||
|
|
||||||
// olmSessionData is a serialized OlmSession
|
// olmSessionData is a serialized OlmSession
|
||||||
internal open class OlmSessionEntity(@PrimaryKey var primaryKey: String = "",
|
internal open class OlmSessionEntity(@PrimaryKey var primaryKey: String = "",
|
||||||
var sessionId: String? = null,
|
var sessionId: String? = null,
|
||||||
var deviceKey: String? = null,
|
var deviceKey: String? = null,
|
||||||
var olmSessionData: String? = null,
|
var olmSessionData: String? = null,
|
||||||
var lastReceivedMessageTs: Long = 0)
|
var lastReceivedMessageTs: Long = 0)
|
||||||
: RealmObject() {
|
: RealmObject() {
|
||||||
|
|
||||||
fun getOlmSession(): OlmSession? {
|
fun getOlmSession(): OlmSession? {
|
||||||
|
@ -138,7 +138,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
|||||||
|
|
||||||
override fun onVerificationAccept(accept: ValidVerificationInfoAccept) {
|
override fun onVerificationAccept(accept: ValidVerificationInfoAccept) {
|
||||||
Timber.v("## SAS O: onVerificationAccept id:$transactionId")
|
Timber.v("## SAS O: onVerificationAccept id:$transactionId")
|
||||||
if (state != VerificationTxState.Started && state != VerificationTxState.SendingStart) {
|
if (state != VerificationTxState.Started && state != VerificationTxState.SendingStart) {
|
||||||
Timber.e("## SAS O: received accept request from invalid state $state")
|
Timber.e("## SAS O: received accept request from invalid state $state")
|
||||||
cancel(CancelCode.UnexpectedMessage)
|
cancel(CancelCode.UnexpectedMessage)
|
||||||
return
|
return
|
||||||
@ -212,7 +212,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
|||||||
// - the Matrix ID of the user who sent the m.key.verification.accept message,
|
// - the Matrix ID of the user who sent the m.key.verification.accept message,
|
||||||
// - he device ID of the device that sent the m.key.verification.accept message
|
// - he device ID of the device that sent the m.key.verification.accept message
|
||||||
// - the transaction ID.
|
// - the transaction ID.
|
||||||
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS$userId$deviceId$otherUserId$otherDeviceId$transactionId"
|
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS$userId$deviceId$otherUserId$otherDeviceId$transactionId"
|
||||||
|
|
||||||
// decimal: generate five bytes by using HKDF.
|
// decimal: generate five bytes by using HKDF.
|
||||||
// emoji: generate six bytes by using HKDF.
|
// emoji: generate six bytes by using HKDF.
|
||||||
@ -220,7 +220,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
|||||||
}
|
}
|
||||||
KEY_AGREEMENT_V2 -> {
|
KEY_AGREEMENT_V2 -> {
|
||||||
// Adds the SAS public key, and separate by |
|
// Adds the SAS public key, and separate by |
|
||||||
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS|$userId|$deviceId|${getSAS().publicKey}|$otherUserId|$otherDeviceId|$otherKey|$transactionId"
|
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS|$userId|$deviceId|${getSAS().publicKey}|$otherUserId|$otherDeviceId|$otherKey|$transactionId"
|
||||||
return getSAS().generateShortCode(sasInfo, 6)
|
return getSAS().generateShortCode(sasInfo, 6)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -57,7 +57,7 @@ internal abstract class DefaultVerificationTransaction(
|
|||||||
|
|
||||||
protected fun trust(canTrustOtherUserMasterKey: Boolean,
|
protected fun trust(canTrustOtherUserMasterKey: Boolean,
|
||||||
toVerifyDeviceIds: List<String>,
|
toVerifyDeviceIds: List<String>,
|
||||||
eventuallyMarkMyMasterKeyAsTrusted: Boolean, autoDone : Boolean = true) {
|
eventuallyMarkMyMasterKeyAsTrusted: Boolean, autoDone: Boolean = true) {
|
||||||
Timber.d("## Verification: trust ($otherUserId,$otherDeviceId) , verifiedDevices:$toVerifyDeviceIds")
|
Timber.d("## Verification: trust ($otherUserId,$otherDeviceId) , verifiedDevices:$toVerifyDeviceIds")
|
||||||
Timber.d("## Verification: trust Mark myMSK trusted $eventuallyMarkMyMasterKeyAsTrusted")
|
Timber.d("## Verification: trust Mark myMSK trusted $eventuallyMarkMyMasterKeyAsTrusted")
|
||||||
|
|
||||||
|
@ -68,10 +68,13 @@ internal abstract class SASDefaultVerificationTransaction(
|
|||||||
// Deprecated maybe removed later, use V2
|
// Deprecated maybe removed later, use V2
|
||||||
const val KEY_AGREEMENT_V1 = "curve25519"
|
const val KEY_AGREEMENT_V1 = "curve25519"
|
||||||
const val KEY_AGREEMENT_V2 = "curve25519-hkdf-sha256"
|
const val KEY_AGREEMENT_V2 = "curve25519-hkdf-sha256"
|
||||||
|
|
||||||
// ordered by preferred order
|
// ordered by preferred order
|
||||||
val KNOWN_AGREEMENT_PROTOCOLS = listOf(KEY_AGREEMENT_V2, KEY_AGREEMENT_V1)
|
val KNOWN_AGREEMENT_PROTOCOLS = listOf(KEY_AGREEMENT_V2, KEY_AGREEMENT_V1)
|
||||||
|
|
||||||
// ordered by preferred order
|
// ordered by preferred order
|
||||||
val KNOWN_HASHES = listOf("sha256")
|
val KNOWN_HASHES = listOf("sha256")
|
||||||
|
|
||||||
// ordered by preferred order
|
// ordered by preferred order
|
||||||
val KNOWN_MACS = listOf(SAS_MAC_SHA256, SAS_MAC_SHA256_LONGKDF)
|
val KNOWN_MACS = listOf(SAS_MAC_SHA256, SAS_MAC_SHA256_LONGKDF)
|
||||||
|
|
||||||
@ -101,6 +104,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
|||||||
|
|
||||||
// Visible for test
|
// Visible for test
|
||||||
var startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null
|
var startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null
|
||||||
|
|
||||||
// Visible for test
|
// Visible for test
|
||||||
var accepted: ValidVerificationInfoAccept? = null
|
var accepted: ValidVerificationInfoAccept? = null
|
||||||
protected var otherKey: String? = null
|
protected var otherKey: String? = null
|
||||||
|
@ -118,7 +118,7 @@ internal class VerificationTransportRoomMessage(
|
|||||||
?.firstOrNull { it.id == enqueueInfo.second }
|
?.firstOrNull { it.id == enqueueInfo.second }
|
||||||
?.let { wInfo ->
|
?.let { wInfo ->
|
||||||
when (wInfo.state) {
|
when (wInfo.state) {
|
||||||
WorkInfo.State.FAILED -> {
|
WorkInfo.State.FAILED -> {
|
||||||
tx?.cancel(onErrorReason)
|
tx?.cancel(onErrorReason)
|
||||||
workLiveData.removeObserver(this)
|
workLiveData.removeObserver(this)
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ internal class VerificationTransportRoomMessage(
|
|||||||
}
|
}
|
||||||
workLiveData.removeObserver(this)
|
workLiveData.removeObserver(this)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ internal class RealmKeysUtils @Inject constructor(context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expose to handle Realm migration to riotX
|
// Expose to handle Realm migration to riotX
|
||||||
fun getRealmEncryptionKey(alias: String) : ByteArray {
|
fun getRealmEncryptionKey(alias: String): ByteArray {
|
||||||
val key = if (hasKeyForDatabase(alias)) {
|
val key = if (hasKeyForDatabase(alias)) {
|
||||||
Timber.i("Found key for alias:$alias")
|
Timber.i("Found key for alias:$alias")
|
||||||
extractKeyForDatabase(alias)
|
extractKeyForDatabase(alias)
|
||||||
|
@ -28,8 +28,8 @@ import kotlinx.coroutines.withContext
|
|||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
|
|
||||||
internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfiguration,
|
internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfiguration,
|
||||||
timeoutMillis: Long,
|
timeoutMillis: Long,
|
||||||
builder: (Realm) -> RealmQuery<T>) {
|
builder: (Realm) -> RealmQuery<T>) {
|
||||||
withTimeout(timeoutMillis) {
|
withTimeout(timeoutMillis) {
|
||||||
// Confine Realm interaction to a single thread with Looper.
|
// Confine Realm interaction to a single thread with Looper.
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
@ -23,12 +23,12 @@ import io.realm.annotations.Index
|
|||||||
import io.realm.annotations.LinkingObjects
|
import io.realm.annotations.LinkingObjects
|
||||||
|
|
||||||
internal open class ChunkEntity(@Index var prevToken: String? = null,
|
internal open class ChunkEntity(@Index var prevToken: String? = null,
|
||||||
// Because of gaps we can have several chunks with nextToken == null
|
// Because of gaps we can have several chunks with nextToken == null
|
||||||
@Index var nextToken: String? = null,
|
@Index var nextToken: String? = null,
|
||||||
var stateEvents: RealmList<EventEntity> = RealmList(),
|
var stateEvents: RealmList<EventEntity> = RealmList(),
|
||||||
var timelineEvents: RealmList<TimelineEventEntity> = RealmList(),
|
var timelineEvents: RealmList<TimelineEventEntity> = RealmList(),
|
||||||
var numberOfTimelineEvents: Long = 0,
|
var numberOfTimelineEvents: Long = 0,
|
||||||
// Only one chunk will have isLastForward == true
|
// Only one chunk will have isLastForward == true
|
||||||
@Index var isLastForward: Boolean = false,
|
@Index var isLastForward: Boolean = false,
|
||||||
@Index var isLastBackward: Boolean = false
|
@Index var isLastBackward: Boolean = false
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user