Merge pull request #2253 from vector-im/feature/bma/small_fixies

Feature/bma/small fixies
This commit is contained in:
Benoit Marty 2020-10-13 11:35:50 +02:00 committed by GitHub
commit 86b4e34031
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 226 additions and 106 deletions

View File

@ -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,14 +50,18 @@ 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())
} }
uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
} }
}
fun getUserAgent() = userAgentHolder.userAgent fun getUserAgent() = userAgentHolder.userAgent

View File

@ -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())
}
fun createCredential() = Credentials(
userId = "userId_" + Random.nextInt(), userId = "userId_" + Random.nextInt(),
homeServer = "http://matrix.org",
accessToken = "access_token",
refreshToken = null,
deviceId = "deviceId_sample" deviceId = "deviceId_sample"
) )
}
} }

View File

@ -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)
}
}

View File

@ -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"
} }
} }

View File

@ -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())
} }

View File

@ -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,7 +41,7 @@ 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,
@ -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)
} }
} }

View File

@ -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()
// } // }
} }

View File

@ -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">

View File

@ -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 {

View File

@ -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
} }
} }
} }
@ -312,7 +315,7 @@ internal class RealmCryptoStore @Inject constructor(
Timber.d("## CrossSigning MSK change for $userId") 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 == credentials.userId) { if (userId == this.userId) {
shouldResetMyDevicesLocalTrust = true shouldResetMyDevicesLocalTrust = true
// my msk has changed! clear my private key // 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 we have some race here? e.g I am the one that did change the keys
@ -331,7 +334,7 @@ internal class RealmCryptoStore @Inject constructor(
Timber.d("## CrossSigning SSK change for $userId") 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 == credentials.userId) { if (userId == this.userId) {
shouldResetMyDevicesLocalTrust = true shouldResetMyDevicesLocalTrust = true
// my ssk has changed! clear my private key // my ssk has changed! clear my private key
realm.where<CryptoMetadataEntity>().findFirst()?.apply { realm.where<CryptoMetadataEntity>().findFirst()?.apply {
@ -349,7 +352,7 @@ internal class RealmCryptoStore @Inject constructor(
Timber.d("## CrossSigning USK change for $userId") 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 == credentials.userId) { if (userId == this.userId) {
shouldResetMyDevicesLocalTrust = true shouldResetMyDevicesLocalTrust = true
// my usk has changed! clear my private key // my usk has changed! clear my private key
realm.where<CryptoMetadataEntity>().findFirst()?.apply { realm.where<CryptoMetadataEntity>().findFirst()?.apply {
@ -362,11 +365,11 @@ internal class RealmCryptoStore @Inject constructor(
// When my cross signing keys are reset, we consider clearing all existing device trust // When my cross signing keys are reset, we consider clearing all existing device trust
if (shouldResetMyDevicesLocalTrust) { if (shouldResetMyDevicesLocalTrust) {
realm.where<UserEntity>() realm.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, credentials.userId) .equalTo(UserEntityFields.USER_ID, this.userId)
.findFirst() .findFirst()
?.devices?.forEach { ?.devices?.forEach {
it?.trustLevelEntity?.crossSignedVerified = false it?.trustLevelEntity?.crossSignedVerified = false
it?.trustLevelEntity?.locallyVerified = it.deviceId == credentials.deviceId it?.trustLevelEntity?.locallyVerified = it.deviceId == deviceId
} }
} }
userEntity.crossSigningInfoEntity = signingInfo userEntity.crossSigningInfoEntity = signingInfo
@ -1355,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
} }
@ -1370,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)

View File

@ -94,6 +94,7 @@ internal class RoomSummaryUpdater @Inject constructor(
// Don't use current state for this one as we are only interested in having MXCRYPTO_ALGORITHM_MEGOLM event in the room // Don't use current state for this one as we are only interested in having MXCRYPTO_ALGORITHM_MEGOLM event in the room
val encryptionEvent = EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION) val encryptionEvent = 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()
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId) val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)

View File

@ -17,13 +17,13 @@
package org.matrix.android.sdk.internal.crypto.verification.qrcode package org.matrix.android.sdk.internal.crypto.verification.qrcode
import org.matrix.android.sdk.MatrixTest import org.matrix.android.sdk.MatrixTest
import org.amshove.kluent.shouldEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.junit.FixMethodOrder import org.junit.FixMethodOrder
import org.junit.Test import org.junit.Test
import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class BinaryStringTest: MatrixTest { class BinaryStringTest : MatrixTest {
/** /**
* I want to put bytes to a String, and vice versa * I want to put bytes to a String, and vice versa
@ -37,17 +37,17 @@ class BinaryStringTest: MatrixTest {
val str = byteArray.toString(Charsets.ISO_8859_1) val str = byteArray.toString(Charsets.ISO_8859_1)
str.length shouldEqualTo 256 str.length shouldBeEqualTo 256
// Ok convert back to bytearray // Ok convert back to bytearray
val result = str.toByteArray(Charsets.ISO_8859_1) val result = str.toByteArray(Charsets.ISO_8859_1)
result.size shouldEqualTo 256 result.size shouldBeEqualTo 256
for (i in 0..255) { for (i in 0..255) {
result[i] shouldEqualTo i.toByte() result[i] shouldBeEqualTo i.toByte()
result[i] shouldEqualTo byteArray[i] result[i] shouldBeEqualTo byteArray[i]
} }
} }
} }

View File

@ -79,9 +79,5 @@ layout_constraintLeft_
### Use im.vector.app.core.preference.VectorPreference to support multiline of the title ### Use im.vector.app.core.preference.VectorPreference to support multiline of the title
<Preference\n <Preference\n
### Will crash on API < 21. Use ?colorAccent instead
\?android:colorAccent
\?android:attr/colorAccent
### Use androidx.recyclerview.widget.RecyclerView because EpoxyRecyclerViews add behavior we do not want to ### Use androidx.recyclerview.widget.RecyclerView because EpoxyRecyclerViews add behavior we do not want to
<com\.airbnb\.epoxy\.EpoxyRecyclerView <com\.airbnb\.epoxy\.EpoxyRecyclerView

View File

@ -44,6 +44,9 @@ class EncryptionItemFactory @Inject constructor(
fun create(event: TimelineEvent, fun create(event: TimelineEvent,
highlight: Boolean, highlight: Boolean,
callback: TimelineEventController.Callback?): StatusTileTimelineItem? { callback: TimelineEventController.Callback?): StatusTileTimelineItem? {
if (!event.root.isStateEvent()) {
return null
}
val algorithm = event.root.getClearContent().toModel<EncryptionEventContent>()?.algorithm val algorithm = event.root.getClearContent().toModel<EncryptionEventContent>()?.algorithm
val informationData = informationDataFactory.create(event, null) val informationData = informationDataFactory.create(event, null)
val attributes = messageItemAttributesFactory.create(null, informationData, callback) val attributes = messageItemAttributesFactory.create(null, informationData, callback)

View File

@ -148,7 +148,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
var hasEncryption = false var hasEncryption = false
var encryptionAlgorithm: String? = null var encryptionAlgorithm: String? = null
while (prevEvent != null && prevEvent.isRoomConfiguration(null)) { while (prevEvent != null && prevEvent.isRoomConfiguration(null)) {
if (prevEvent.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION) { if (prevEvent.root.isStateEvent() && prevEvent.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION) {
hasEncryption = true hasEncryption = true
encryptionAlgorithm = prevEvent.root.getClearContent()?.toModel<EncryptionEventContent>()?.algorithm encryptionAlgorithm = prevEvent.root.getClearContent()?.toModel<EncryptionEventContent>()?.algorithm
} }

View File

@ -428,6 +428,9 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour
} }
private fun formatRoomEncryptionEvent(event: Event, senderName: String?): CharSequence? { private fun formatRoomEncryptionEvent(event: Event, senderName: String?): CharSequence? {
if (!event.isStateEvent()) {
return null
}
val content = event.content.toModel<EncryptionEventContent>() ?: return null val content = event.content.toModel<EncryptionEventContent>() ?: return null
return when (content.algorithm) { return when (content.algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM -> MXCRYPTO_ALGORITHM_MEGOLM ->

View File

@ -55,7 +55,7 @@ fun TimelineEvent.canBeMerged(): Boolean {
} }
fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean { fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean {
return when (root.getClearType()) { return root.isStateEvent() && when (root.getClearType()) {
EventType.STATE_ROOM_GUEST_ACCESS, EventType.STATE_ROOM_GUEST_ACCESS,
EventType.STATE_ROOM_HISTORY_VISIBILITY, EventType.STATE_ROOM_HISTORY_VISIBILITY,
EventType.STATE_ROOM_JOIN_RULES, EventType.STATE_ROOM_JOIN_RULES,

View File

@ -58,9 +58,6 @@ class RoomMemberListFragment @Inject constructor(
setupSearchView() setupSearchView()
setupInviteUsersButton() setupInviteUsersButton()
recyclerView.configureWith(roomMemberListController, hasFixedSize = true) recyclerView.configureWith(roomMemberListController, hasFixedSize = true)
viewModel.selectSubscribe(this, RoomMemberListViewState::actionsPermissions) {
invalidateOptionsMenu()
}
} }
private fun setupInviteUsersButton() { private fun setupInviteUsersButton() {
@ -88,7 +85,6 @@ class RoomMemberListFragment @Inject constructor(
} }
private fun setupSearchView() { private fun setupSearchView() {
searchViewAppBarLayout.isVisible = true
searchView.queryHint = getString(R.string.search_members_hint) searchView.queryHint = getString(R.string.search_members_hint)
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
@ -111,6 +107,8 @@ class RoomMemberListFragment @Inject constructor(
roomMemberListController.setData(viewState) roomMemberListController.setData(viewState)
renderRoomSummary(viewState) renderRoomSummary(viewState)
inviteUsersButton.isVisible = viewState.actionsPermissions.canInvite inviteUsersButton.isVisible = viewState.actionsPermissions.canInvite
// Display filter only if there are more than 2 members in this room
searchViewAppBarLayout.isVisible = viewState.roomSummary()?.otherMemberIds.orEmpty().size > 1
} }
override fun onRoomMemberClicked(roomMember: RoomMemberSummary) { override fun onRoomMemberClicked(roomMember: RoomMemberSummary) {

View File

@ -10,7 +10,11 @@
android:id="@+id/expandableContent" android:id="@+id/expandableContent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autoLink="web"
android:ellipsize="end" android:ellipsize="end"
android:fontFamily="sans-serif"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintBottom_toTopOf="@+id/expandableArrow" app:layout_constraintBottom_toTopOf="@+id/expandableArrow"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -23,12 +27,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:autoLink="web"
android:fontFamily="sans-serif"
android:gravity="center"
android:src="@drawable/ic_expand_more" android:src="@drawable/ic_expand_more"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/expandableContent" app:layout_constraintTop_toBottomOf="@+id/expandableContent"