valuePredicate) {
+ int size = valueHistory.size();
+ for (int valueIndex = 0; valueIndex < size; valueIndex++) {
+ T value = this.valueHistory.get(valueIndex);
+ if (valuePredicate.apply(value)) {
+ throw fail("Value at position " + valueIndex + " matches predicate "
+ + valuePredicate.toString() + ", which was not expected.");
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Awaits until this TestObserver has any value.
+ *
+ * If this TestObserver has already value then this method returns immediately.
+ *
+ * @return this
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ */
+ public LiveDataTestObserver awaitValue() throws InterruptedException {
+ valueLatch.await();
+ return this;
+ }
+
+ /**
+ * Awaits the specified amount of time or until this TestObserver has any value.
+ *
+ * If this TestObserver has already value then this method returns immediately.
+ *
+ * @return this
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ */
+ public LiveDataTestObserver awaitValue(long timeout, TimeUnit timeUnit) throws InterruptedException {
+ valueLatch.await(timeout, timeUnit);
+ return this;
+ }
+
+ /**
+ * Awaits until this TestObserver receives next value.
+ *
+ * If this TestObserver has already value then it awaits for another one.
+ *
+ * @return this
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ */
+ public LiveDataTestObserver awaitNextValue() throws InterruptedException {
+ return withNewLatch().awaitValue();
+ }
+
+
+ /**
+ * Awaits the specified amount of time or until this TestObserver receives next value.
+ *
+ * If this TestObserver has already value then it awaits for another one.
+ *
+ * @return this
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ */
+ public LiveDataTestObserver awaitNextValue(long timeout, TimeUnit timeUnit) throws InterruptedException {
+ return withNewLatch().awaitValue(timeout, timeUnit);
+ }
+
+ private LiveDataTestObserver withNewLatch() {
+ valueLatch = new CountDownLatch(1);
+ return this;
+ }
+
+ private AssertionError fail(String message) {
+ return new AssertionError(message);
+ }
+
+ private static String valueAndClass(Object value) {
+ if (value != null) {
+ return value + " (class: " + value.getClass().getSimpleName() + ")";
+ }
+ return "null";
+ }
+
+ public static LiveDataTestObserver create() {
+ return new LiveDataTestObserver<>(new MutableLiveData());
+ }
+
+ public static LiveDataTestObserver test(LiveData liveData) {
+ LiveDataTestObserver observer = new LiveDataTestObserver<>(liveData);
+ liveData.observeForever(observer);
+ return observer;
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/MainThreadExecutor.java b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/MainThreadExecutor.java
new file mode 100644
index 0000000000..7eb119ce7d
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/MainThreadExecutor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.concurrent.Executor;
+
+public class MainThreadExecutor implements Executor {
+
+ private final Handler handler = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void execute(Runnable runnable) {
+ handler.post(runnable);
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/OkReplayRuleChainNoActivity.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/OkReplayRuleChainNoActivity.kt
new file mode 100644
index 0000000000..9fd1fe6b5c
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/OkReplayRuleChainNoActivity.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android
+
+import okreplay.OkReplayConfig
+import okreplay.PermissionRule
+import okreplay.RecorderRule
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+
+class OkReplayRuleChainNoActivity(
+ private val configuration: OkReplayConfig) {
+
+ fun get(): TestRule {
+ return RuleChain.outerRule(PermissionRule(configuration))
+ .around(RecorderRule(configuration))
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/SingleThreadCoroutineDispatcher.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/SingleThreadCoroutineDispatcher.kt
new file mode 100644
index 0000000000..69803c5dc1
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/SingleThreadCoroutineDispatcher.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android
+
+import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
+import kotlinx.coroutines.Dispatchers.Main
+
+internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, Main)
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/auth/AuthenticatorTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/auth/AuthenticatorTest.kt
new file mode 100644
index 0000000000..3dfc06196e
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/auth/AuthenticatorTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.auth
+
+import androidx.test.annotation.UiThreadTest
+import androidx.test.rule.GrantPermissionRule
+import androidx.test.runner.AndroidJUnit4
+import im.vector.matrix.android.InstrumentedTest
+import im.vector.matrix.android.OkReplayRuleChainNoActivity
+import im.vector.matrix.android.api.auth.Authenticator
+import okreplay.*
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@RunWith(AndroidJUnit4::class)
+internal class AuthenticatorTest : InstrumentedTest {
+
+ lateinit var authenticator: Authenticator
+ lateinit var okReplayInterceptor: OkReplayInterceptor
+
+ private val okReplayConfig = OkReplayConfig.Builder()
+ .tapeRoot(AndroidTapeRoot(
+ context(), javaClass))
+ .defaultMode(TapeMode.READ_WRITE) // or TapeMode.READ_ONLY
+ .sslEnabled(true)
+ .interceptor(okReplayInterceptor)
+ .build()
+
+ @get:Rule
+ val testRule = OkReplayRuleChainNoActivity(okReplayConfig).get()
+
+ @Test
+ @UiThreadTest
+ @OkReplay(tape = "auth", mode = TapeMode.READ_WRITE)
+ fun auth() {
+
+ }
+
+ companion object {
+ @ClassRule
+ @JvmField
+ val grantExternalStoragePermissionRule: GrantPermissionRule =
+ GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ }
+
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/CryptoStoreHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/CryptoStoreHelper.kt
new file mode 100644
index 0000000000..cea612dd92
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/CryptoStoreHelper.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.crypto
+
+import im.vector.matrix.android.api.auth.data.Credentials
+import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
+import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
+import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
+import io.realm.RealmConfiguration
+import java.util.*
+
+internal class CryptoStoreHelper {
+
+ fun createStore(): IMXCryptoStore {
+ return RealmCryptoStore(
+ realmConfiguration = RealmConfiguration.Builder()
+ .name("test.realm")
+ .modules(RealmCryptoStoreModule())
+ .build(),
+ credentials = createCredential())
+ }
+
+ fun createCredential() = Credentials(
+ userId = "userId_" + Random().nextInt(),
+ homeServer = "http://matrix.org",
+ accessToken = "access_token",
+ refreshToken = null,
+ deviceId = "deviceId_sample"
+ )
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/CryptoStoreTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/CryptoStoreTest.kt
new file mode 100644
index 0000000000..0f65ad3ee5
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/CryptoStoreTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.crypto
+
+import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
+import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
+import org.junit.Assert.*
+import org.junit.Test
+import org.matrix.olm.OlmAccount
+import org.matrix.olm.OlmManager
+import org.matrix.olm.OlmSession
+
+private const val DUMMY_DEVICE_KEY = "DeviceKey"
+
+class CryptoStoreTest {
+
+ private val cryptoStoreHelper = CryptoStoreHelper()
+
+ @Test
+ fun test_metadata_realm_ok() {
+ val cryptoStore: IMXCryptoStore = cryptoStoreHelper.createStore()
+
+ assertFalse(cryptoStore.hasData())
+
+ cryptoStore.open()
+
+ assertEquals("deviceId_sample", cryptoStore.getDeviceId())
+
+ assertTrue(cryptoStore.hasData())
+
+ // Cleanup
+ cryptoStore.close()
+ cryptoStore.deleteStore()
+ }
+
+ @Test
+ fun test_lastSessionUsed() {
+ // Ensure Olm is initialized
+ OlmManager()
+
+ val cryptoStore: IMXCryptoStore = cryptoStoreHelper.createStore()
+
+ assertNull(cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY))
+
+ val olmAccount1 = OlmAccount().apply {
+ generateOneTimeKeys(1)
+ }
+
+ val olmSession1 = OlmSession().apply {
+ initOutboundSession(olmAccount1,
+ olmAccount1.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY],
+ olmAccount1.oneTimeKeys()[OlmAccount.JSON_KEY_ONE_TIME_KEY]?.values?.first())
+ }
+
+ val sessionId1 = olmSession1.sessionIdentifier()
+ val olmSessionWrapper1 = OlmSessionWrapper(olmSession1)
+
+ cryptoStore.storeSession(olmSessionWrapper1, DUMMY_DEVICE_KEY)
+
+ assertEquals(sessionId1, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY))
+
+ val olmAccount2 = OlmAccount().apply {
+ generateOneTimeKeys(1)
+ }
+
+ val olmSession2 = OlmSession().apply {
+ initOutboundSession(olmAccount2,
+ olmAccount2.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY],
+ olmAccount2.oneTimeKeys()[OlmAccount.JSON_KEY_ONE_TIME_KEY]?.values?.first())
+ }
+
+ val sessionId2 = olmSession2.sessionIdentifier()
+ val olmSessionWrapper2 = OlmSessionWrapper(olmSession2)
+
+ cryptoStore.storeSession(olmSessionWrapper2, DUMMY_DEVICE_KEY)
+
+ // Ensure sessionIds are distinct
+ assertNotEquals(sessionId1, sessionId2)
+
+ // Note: we cannot be sure what will be the result of getLastUsedSessionId() here
+
+ olmSessionWrapper2.onMessageReceived()
+ cryptoStore.storeSession(olmSessionWrapper2, DUMMY_DEVICE_KEY)
+
+ // sessionId2 is returned now
+ assertEquals(sessionId2, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY))
+
+ Thread.sleep(2)
+
+ olmSessionWrapper1.onMessageReceived()
+ cryptoStore.storeSession(olmSessionWrapper1, DUMMY_DEVICE_KEY)
+
+ // sessionId1 is returned now
+ assertEquals(sessionId1, cryptoStore.getLastUsedSessionId(DUMMY_DEVICE_KEY))
+
+ // Cleanup
+ olmSession1.releaseSession()
+ olmSession2.releaseSession()
+
+ olmAccount1.releaseAccount()
+ olmAccount2.releaseAccount()
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/util/JsonCanonicalizerTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/util/JsonCanonicalizerTest.kt
new file mode 100644
index 0000000000..1ea6105da4
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/util/JsonCanonicalizerTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.util
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import im.vector.matrix.android.InstrumentedTest
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+internal class JsonCanonicalizerTest : InstrumentedTest {
+
+ @Test
+ fun identityTest() {
+ listOf(
+ "{}",
+ """{"a":true}""",
+ """{"a":false}""",
+ """{"a":1}""",
+ """{"a":1.2}""",
+ """{"a":null}""",
+ """{"a":[]}""",
+ """{"a":["b":"c"]}""",
+ """{"a":["c":"b","d":"e"]}""",
+ """{"a":["d":"b","c":"e"]}"""
+ ).forEach {
+ assertEquals(it,
+ JsonCanonicalizer.canonicalize(it))
+ }
+ }
+
+ @Test
+ fun reorderTest() {
+ assertEquals("""{"a":true,"b":false}""",
+ JsonCanonicalizer.canonicalize("""{"b":false,"a":true}"""))
+ }
+
+ @Test
+ fun realSampleTest() {
+ assertEquals("""{"algorithms":["m.megolm.v1.aes-sha2","m.olm.v1.curve25519-aes-sha2"],"device_id":"VSCUNFSOUI","keys":{"curve25519:VSCUNFSOUI":"utyOjnhiQ73qNhi9HlN0OgWIowe5gthTS8r0r9TcJ3o","ed25519:VSCUNFSOUI":"qNhEt+Yggaajet0hX\/FjTRLfySgs65ldYyomm7PIx6U"},"user_id":"@benoitx:matrix.org"}""",
+ JsonCanonicalizer.canonicalize("""{"algorithms":["m.megolm.v1.aes-sha2","m.olm.v1.curve25519-aes-sha2"],"device_id":"VSCUNFSOUI","user_id":"@benoitx:matrix.org","keys":{"curve25519:VSCUNFSOUI":"utyOjnhiQ73qNhi9HlN0OgWIowe5gthTS8r0r9TcJ3o","ed25519:VSCUNFSOUI":"qNhEt+Yggaajet0hX/FjTRLfySgs65ldYyomm7PIx6U"}}"""))
+ }
+
+ @Test
+ fun doubleQuoteTest() {
+ assertEquals("{\"a\":\"\\\"\"}",
+ JsonCanonicalizer.canonicalize("{\"a\":\"\\\"\"}"))
+ }
+
+
+
+ /* ==========================================================================================
+ * Test from https://matrix.org/docs/spec/appendices.html#examples
+ * ========================================================================================== */
+
+ @Test
+ fun matrixOrg001Test() {
+ assertEquals("""{}""",
+ JsonCanonicalizer.canonicalize("""{}"""))
+ }
+
+
+ @Test
+ fun matrixOrg002Test() {
+ assertEquals("""{"one":1,"two":"Two"}""",
+ JsonCanonicalizer.canonicalize("""{
+ "one": 1,
+ "two": "Two"
+}"""))
+ }
+
+
+ @Test
+ fun matrixOrg003Test() {
+ assertEquals("""{"a":"1","b":"2"}""",
+ JsonCanonicalizer.canonicalize("""{
+ "b": "2",
+ "a": "1"
+}"""))
+ }
+
+
+ @Test
+ fun matrixOrg004Test() {
+ assertEquals("""{"a":"1","b":"2"}""",
+ JsonCanonicalizer.canonicalize("""{"b":"2","a":"1"}"""))
+ }
+
+
+ @Test
+ fun matrixOrg005Test() {
+ assertEquals("""{"auth":{"mxid":"@john.doe:example.com","profile":{"display_name":"John Doe","three_pids":[{"address":"john.doe@example.org","medium":"email"},{"address":"123456789","medium":"msisdn"}]},"success":true}}""",
+ JsonCanonicalizer.canonicalize("""{
+ "auth": {
+ "success": true,
+ "mxid": "@john.doe:example.com",
+ "profile": {
+ "display_name": "John Doe",
+ "three_pids": [
+ {
+ "medium": "email",
+ "address": "john.doe@example.org"
+ },
+ {
+ "medium": "msisdn",
+ "address": "123456789"
+ }
+ ]
+ }
+ }
+}"""))
+ }
+
+
+ @Test
+ fun matrixOrg006Test() {
+ assertEquals("""{"a":"日本語"}""",
+ JsonCanonicalizer.canonicalize("""{
+ "a": "日本語"
+}"""))
+ }
+
+
+ @Test
+ fun matrixOrg007Test() {
+ assertEquals("""{"日":1,"本":2}""",
+ JsonCanonicalizer.canonicalize("""{
+ "本": 2,
+ "日": 1
+}"""))
+ }
+
+
+ @Test
+ fun matrixOrg008Test() {
+ assertEquals("""{"a":"日"}""",
+ JsonCanonicalizer.canonicalize("{\"a\": \"\u65E5\"}"))
+ }
+
+ @Test
+ fun matrixOrg009Test() {
+ assertEquals("""{"a":null}""",
+ JsonCanonicalizer.canonicalize("""{
+ "a": null
+}"""))
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt
new file mode 100644
index 0000000000..be351fb03c
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.session.room.timeline
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.InstrumentedTest
+import im.vector.matrix.android.internal.database.helper.add
+import im.vector.matrix.android.internal.database.helper.addAll
+import im.vector.matrix.android.internal.database.helper.isUnlinked
+import im.vector.matrix.android.internal.database.helper.lastStateIndex
+import im.vector.matrix.android.internal.database.helper.merge
+import im.vector.matrix.android.internal.database.model.ChunkEntity
+import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
+import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeListOfEvents
+import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeMessageEvent
+import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeRoomMemberEvent
+import io.realm.Realm
+import io.realm.RealmConfiguration
+import io.realm.kotlin.createObject
+import org.amshove.kluent.shouldBeFalse
+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)
+internal class ChunkEntityTest : InstrumentedTest {
+
+ private lateinit var monarchy: Monarchy
+
+ @Before
+ fun setup() {
+ Realm.init(context())
+ val testConfig = RealmConfiguration.Builder().inMemory().name("test-realm").build()
+ monarchy = Monarchy.Builder().setRealmConfiguration(testConfig).build()
+ }
+
+
+ @Test
+ fun add_shouldAdd_whenNotAlreadyIncluded() {
+ monarchy.runTransactionSync { realm ->
+ val chunk: ChunkEntity = realm.createObject()
+ val fakeEvent = createFakeMessageEvent()
+ chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
+ chunk.timelineEvents.size shouldEqual 1
+ }
+ }
+
+ @Test
+ fun add_shouldNotAdd_whenAlreadyIncluded() {
+ monarchy.runTransactionSync { realm ->
+ val chunk: ChunkEntity = realm.createObject()
+ val fakeEvent = createFakeMessageEvent()
+ chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
+ chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
+ chunk.timelineEvents.size shouldEqual 1
+ }
+ }
+
+ @Test
+ fun add_shouldStateIndexIncremented_whenStateEventIsAddedForward() {
+ monarchy.runTransactionSync { realm ->
+ val chunk: ChunkEntity = realm.createObject()
+ val fakeEvent = createFakeRoomMemberEvent()
+ chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
+ chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual 1
+ }
+ }
+
+ @Test
+ fun add_shouldStateIndexNotIncremented_whenNoStateEventIsAdded() {
+ monarchy.runTransactionSync { realm ->
+ val chunk: ChunkEntity = realm.createObject()
+ val fakeEvent = createFakeMessageEvent()
+ chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
+ chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual 0
+ }
+ }
+
+ @Test
+ fun addAll_shouldStateIndexIncremented_whenStateEventsAreAddedForward() {
+ monarchy.runTransactionSync { realm ->
+ val chunk: ChunkEntity = realm.createObject()
+ val fakeEvents = createFakeListOfEvents(30)
+ val numberOfStateEvents = fakeEvents.filter { it.isStateEvent() }.size
+ chunk.addAll("roomId", fakeEvents, PaginationDirection.FORWARDS)
+ chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual numberOfStateEvents
+ }
+ }
+
+ @Test
+ fun addAll_shouldStateIndexDecremented_whenStateEventsAreAddedBackward() {
+ monarchy.runTransactionSync { realm ->
+ val chunk: ChunkEntity = realm.createObject()
+ val fakeEvents = createFakeListOfEvents(30)
+ val numberOfStateEvents = fakeEvents.filter { it.isStateEvent() }.size
+ val lastIsState = fakeEvents.last().isStateEvent()
+ val expectedStateIndex = if (lastIsState) -numberOfStateEvents + 1 else -numberOfStateEvents
+ chunk.addAll("roomId", fakeEvents, PaginationDirection.BACKWARDS)
+ chunk.lastStateIndex(PaginationDirection.BACKWARDS) shouldEqual expectedStateIndex
+ }
+ }
+
+ @Test
+ fun merge_shouldAddEvents_whenMergingBackward() {
+ monarchy.runTransactionSync { realm ->
+ val chunk1: ChunkEntity = realm.createObject()
+ val chunk2: ChunkEntity = realm.createObject()
+ chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
+ chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
+ chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
+ chunk1.timelineEvents.size shouldEqual 60
+ }
+ }
+
+ @Test
+ fun merge_shouldAddOnlyDifferentEvents_whenMergingBackward() {
+ monarchy.runTransactionSync { realm ->
+ val chunk1: ChunkEntity = realm.createObject()
+ val chunk2: ChunkEntity = realm.createObject()
+ val eventsForChunk1 = createFakeListOfEvents(30)
+ val eventsForChunk2 = eventsForChunk1 + createFakeListOfEvents(10)
+ chunk1.isLastForward = true
+ chunk2.isLastForward = false
+ chunk1.addAll("roomId", eventsForChunk1, PaginationDirection.FORWARDS)
+ chunk2.addAll("roomId", eventsForChunk2, PaginationDirection.BACKWARDS)
+ chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
+ chunk1.timelineEvents.size shouldEqual 40
+ chunk1.isLastForward.shouldBeTrue()
+ }
+ }
+
+ @Test
+ fun merge_shouldEventsBeLinked_whenMergingLinkedWithUnlinked() {
+ monarchy.runTransactionSync { realm ->
+ val chunk1: ChunkEntity = realm.createObject()
+ val chunk2: ChunkEntity = realm.createObject()
+ chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
+ chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = false)
+ chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
+ chunk1.isUnlinked().shouldBeFalse()
+ }
+ }
+
+ @Test
+ fun merge_shouldEventsBeUnlinked_whenMergingUnlinkedWithUnlinked() {
+ monarchy.runTransactionSync { realm ->
+ val chunk1: ChunkEntity = realm.createObject()
+ val chunk2: ChunkEntity = realm.createObject()
+ chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
+ chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
+ chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
+ chunk1.isUnlinked().shouldBeTrue()
+ }
+ }
+
+ @Test
+ fun merge_shouldPrevTokenMerged_whenMergingForwards() {
+ monarchy.runTransactionSync { realm ->
+ val chunk1: ChunkEntity = realm.createObject()
+ val chunk2: ChunkEntity = realm.createObject()
+ val prevToken = "prev_token"
+ chunk1.prevToken = prevToken
+ chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
+ chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
+ chunk1.merge("roomId", chunk2, PaginationDirection.FORWARDS)
+ chunk1.prevToken shouldEqual prevToken
+ }
+ }
+
+ @Test
+ fun merge_shouldNextTokenMerged_whenMergingBackwards() {
+ monarchy.runTransactionSync { realm ->
+ val chunk1: ChunkEntity = realm.createObject()
+ val chunk2: ChunkEntity = realm.createObject()
+ val nextToken = "next_token"
+ chunk1.nextToken = nextToken
+ chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
+ chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
+ chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
+ chunk1.nextToken shouldEqual nextToken
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt
new file mode 100644
index 0000000000..f77c9b1a0c
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.session.room.timeline
+
+import arrow.core.Try
+import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask
+import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
+import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
+import kotlin.random.Random
+
+internal class FakeGetContextOfEventTask constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask {
+
+ override suspend fun execute(params: GetContextOfEventTask.Params): Try {
+ val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
+ val tokenChunkEvent = FakeTokenChunkEvent(
+ Random.nextLong(System.currentTimeMillis()).toString(),
+ Random.nextLong(System.currentTimeMillis()).toString(),
+ fakeEvents
+ )
+ return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, PaginationDirection.BACKWARDS)
+ }
+
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt
new file mode 100644
index 0000000000..bf16340134
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.session.room.timeline
+
+import arrow.core.Try
+import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
+import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
+import javax.inject.Inject
+import kotlin.random.Random
+
+internal class FakePaginationTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask {
+
+ override suspend fun execute(params: PaginationTask.Params): Try {
+ val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
+ val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
+ return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
+ }
+
+}
+
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeTokenChunkEvent.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeTokenChunkEvent.kt
new file mode 100644
index 0000000000..2a9163e4ba
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeTokenChunkEvent.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.session.room.timeline
+
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEvent
+
+internal data class FakeTokenChunkEvent(override val start: String?,
+ override val end: String?,
+ override val events: List = emptyList(),
+ override val stateEvents: List = emptyList()
+) : TokenChunkEvent
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt
new file mode 100644
index 0000000000..7ff693584e
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.session.room.timeline
+
+import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.api.session.events.model.Content
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.events.model.EventType
+import im.vector.matrix.android.api.session.events.model.toContent
+import im.vector.matrix.android.api.session.room.model.Membership
+import im.vector.matrix.android.api.session.room.model.RoomMember
+import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
+import im.vector.matrix.android.api.session.room.model.message.MessageType
+import im.vector.matrix.android.internal.database.helper.addAll
+import im.vector.matrix.android.internal.database.helper.addOrUpdate
+import im.vector.matrix.android.internal.database.model.ChunkEntity
+import im.vector.matrix.android.internal.database.model.RoomEntity
+import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
+import io.realm.kotlin.createObject
+import kotlin.random.Random
+
+object RoomDataHelper {
+
+ private const val FAKE_TEST_SENDER = "@sender:test.org"
+ private val EVENT_FACTORIES = hashMapOf(
+ 0 to { createFakeMessageEvent() },
+ 1 to { createFakeRoomMemberEvent() }
+ )
+
+ fun createFakeListOfEvents(size: Int = 10): List {
+ return (0 until size).mapNotNull {
+ val nextInt = Random.nextInt(EVENT_FACTORIES.size)
+ EVENT_FACTORIES[nextInt]?.invoke()
+ }
+ }
+
+ fun createFakeEvent(type: String,
+ content: Content? = null,
+ prevContent: Content? = null,
+ sender: String = FAKE_TEST_SENDER,
+ stateKey: String = FAKE_TEST_SENDER
+ ): Event {
+ return Event(
+ type = type,
+ eventId = Random.nextLong().toString(),
+ content = content,
+ prevContent = prevContent,
+ senderId = sender,
+ stateKey = stateKey
+ )
+ }
+
+ fun createFakeMessageEvent(): Event {
+ val message = MessageTextContent(MessageType.MSGTYPE_TEXT, "Fake message #${Random.nextLong()}").toContent()
+ return createFakeEvent(EventType.MESSAGE, message)
+ }
+
+ fun createFakeRoomMemberEvent(): Event {
+ val roomMember = RoomMember(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
+ return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
+ }
+
+ fun fakeInitialSync(monarchy: Monarchy, roomId: String) {
+ monarchy.runTransactionSync { realm ->
+ val roomEntity = realm.createObject(roomId)
+ roomEntity.membership = Membership.JOIN
+ val eventList = createFakeListOfEvents(10)
+ val chunkEntity = realm.createObject().apply {
+ nextToken = null
+ prevToken = Random.nextLong(System.currentTimeMillis()).toString()
+ isLastForward = true
+ }
+ chunkEntity.addAll(roomId, eventList, PaginationDirection.FORWARDS)
+ roomEntity.addOrUpdate(chunkEntity)
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt
new file mode 100644
index 0000000000..f09b037605
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.session.room.timeline
+
+import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.InstrumentedTest
+
+internal class TimelineTest : InstrumentedTest {
+
+ companion object {
+ private const val ROOM_ID = "roomId"
+ }
+
+ private lateinit var monarchy: Monarchy
+
+// @Before
+// fun setup() {
+// Timber.plant(Timber.DebugTree())
+// Realm.init(context())
+// val testConfiguration = RealmConfiguration.Builder().name("test-realm")
+// .modules(SessionRealmModule()).build()
+//
+// Realm.deleteRealm(testConfiguration)
+// monarchy = Monarchy.Builder().setRealmConfiguration(testConfiguration).build()
+// RoomDataHelper.fakeInitialSync(monarchy, ROOM_ID)
+// }
+//
+// private fun createTimeline(initialEventId: String? = null): Timeline {
+// val taskExecutor = TaskExecutor(testCoroutineDispatchers)
+// val tokenChunkEventPersistor = TokenChunkEventPersistor(monarchy)
+// val paginationTask = FakePaginationTask @Inject constructor(tokenChunkEventPersistor)
+// val getContextOfEventTask = FakeGetContextOfEventTask @Inject constructor(tokenChunkEventPersistor)
+// val roomMemberExtractor = SenderRoomMemberExtractor(ROOM_ID)
+// val timelineEventFactory = TimelineEventFactory(roomMemberExtractor, EventRelationExtractor())
+// return DefaultTimeline(
+// ROOM_ID,
+// initialEventId,
+// monarchy.realmConfiguration,
+// taskExecutor,
+// getContextOfEventTask,
+// timelineEventFactory,
+// paginationTask,
+// null)
+// }
+//
+// @Test
+// fun backPaginate_shouldLoadMoreEvents_whenPaginateIsCalled() {
+// val timeline = createTimeline()
+// timeline.start()
+// val paginationCount = 30
+// var initialLoad = 0
+// val latch = CountDownLatch(2)
+// var timelineEvents: List = emptyList()
+// timeline.listener = object : Timeline.Listener {
+// override fun onUpdated(snapshot: List) {
+// if (snapshot.isNotEmpty()) {
+// if (initialLoad == 0) {
+// initialLoad = snapshot.size
+// }
+// timelineEvents = snapshot
+// latch.countDown()
+// timeline.paginate(Timeline.Direction.BACKWARDS, paginationCount)
+// }
+// }
+// }
+// latch.await()
+// timelineEvents.size shouldEqual initialLoad + paginationCount
+// timeline.dispose()
+// }
+
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/CurlLoggingInterceptor.kt b/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/CurlLoggingInterceptor.kt
new file mode 100644
index 0000000000..3d499be3c1
--- /dev/null
+++ b/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/CurlLoggingInterceptor.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 Jeff Gilfelt.
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.network.interceptors
+
+import im.vector.matrix.android.internal.di.MatrixScope
+import okhttp3.Interceptor
+import okhttp3.Response
+import okhttp3.logging.HttpLoggingInterceptor
+import okio.Buffer
+import java.io.IOException
+import java.nio.charset.Charset
+import javax.inject.Inject
+
+/**
+ * An OkHttp interceptor that logs requests as curl shell commands. They can then
+ * be copied, pasted and executed inside a terminal environment. This might be
+ * useful for troubleshooting client/server API interaction during development,
+ * making it easy to isolate and share requests made by the app. Warning: The
+ * logs generated by this interceptor have the potential to leak sensitive
+ * information. It should only be used in a controlled manner or in a
+ * non-production environment.
+ */
+@MatrixScope
+internal class CurlLoggingInterceptor @Inject constructor(private val logger: HttpLoggingInterceptor.Logger)
+ : Interceptor {
+
+ /**
+ * Set any additional curl command options (see 'curl --help').
+ */
+ var curlOptions: String? = null
+
+ @Throws(IOException::class)
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val request = chain.request()
+
+ var compressed = false
+
+ var curlCmd = "curl"
+ if (curlOptions != null) {
+ curlCmd += " " + curlOptions!!
+ }
+ curlCmd += " -X " + request.method()
+
+ val requestBody = request.body()
+ if (requestBody != null) {
+ val buffer = Buffer()
+ requestBody.writeTo(buffer)
+ var charset: Charset? = UTF8
+ val contentType = requestBody.contentType()
+ if (contentType != null) {
+ charset = contentType.charset(UTF8)
+ }
+ // try to keep to a single line and use a subshell to preserve any line breaks
+ curlCmd += " --data $'" + buffer.readString(charset!!).replace("\n", "\\n") + "'"
+ }
+
+ val headers = request.headers()
+ var i = 0
+ val count = headers.size()
+ while (i < count) {
+ val name = headers.name(i)
+ val value = headers.value(i)
+ if ("Accept-Encoding".equals(name, ignoreCase = true) && "gzip".equals(value, ignoreCase = true)) {
+ compressed = true
+ }
+ curlCmd += " -H \"$name: $value\""
+ i++
+ }
+
+ curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url().toString()
+ // Replace localhost for emulator by localhost for shell
+ .replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/")
+ + "'")
+
+ // Add Json formatting
+ curlCmd += " | python -m json.tool"
+
+ logger.log("--- cURL (" + request.url() + ")")
+ logger.log(curlCmd)
+
+ return chain.proceed(request)
+ }
+
+ companion object {
+ private val UTF8 = Charset.forName("UTF-8")
+ }
+}
diff --git a/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/FormattedJsonHttpLogger.kt b/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/FormattedJsonHttpLogger.kt
new file mode 100644
index 0000000000..655134d1e3
--- /dev/null
+++ b/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/FormattedJsonHttpLogger.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.network.interceptors
+
+import androidx.annotation.NonNull
+import im.vector.matrix.android.BuildConfig
+import okhttp3.logging.HttpLoggingInterceptor
+import org.json.JSONArray
+import org.json.JSONException
+import org.json.JSONObject
+import timber.log.Timber
+
+class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
+
+ companion object {
+ private const val INDENT_SPACE = 2
+ }
+
+ /**
+ * Log the message and try to log it again as a JSON formatted string
+ * Note: it can consume a lot of memory but it is only in DEBUG mode
+ *
+ * @param message
+ */
+ @Synchronized
+ override fun log(@NonNull message: String) {
+ // In RELEASE there is no log, but for sure, test again BuildConfig.DEBUG
+ if (BuildConfig.DEBUG) {
+ Timber.v(message)
+
+ if (message.startsWith("{")) {
+ // JSON Detected
+ try {
+ val o = JSONObject(message)
+ logJson(o.toString(INDENT_SPACE))
+ } catch (e: JSONException) {
+ // Finally this is not a JSON string...
+ Timber.e(e)
+ }
+
+ } else if (message.startsWith("[")) {
+ // JSON Array detected
+ try {
+ val o = JSONArray(message)
+ logJson(o.toString(INDENT_SPACE))
+ } catch (e: JSONException) {
+ // Finally not JSON...
+ Timber.e(e)
+ }
+
+ }
+ // Else not a json string to log
+ }
+ }
+
+ private fun logJson(formattedJson: String) {
+ val arr = formattedJson.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ for (s in arr) {
+ Timber.v(s)
+ }
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/AndroidManifest.xml b/matrix-sdk-android/src/main/AndroidManifest.xml
index 90cd94ab3e..7191d9c8d5 100644
--- a/matrix-sdk-android/src/main/AndroidManifest.xml
+++ b/matrix-sdk-android/src/main/AndroidManifest.xml
@@ -1,2 +1,19 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ package="im.vector.matrix.android">
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt
new file mode 100644
index 0000000000..bdc988d2f7
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api
+
+import android.content.Context
+import androidx.lifecycle.ProcessLifecycleOwner
+import androidx.work.Configuration
+import androidx.work.WorkManager
+import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.BuildConfig
+import im.vector.matrix.android.api.auth.Authenticator
+import im.vector.matrix.android.internal.SessionManager
+import im.vector.matrix.android.internal.di.DaggerMatrixComponent
+import im.vector.matrix.android.internal.network.UserAgentHolder
+import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
+import org.matrix.olm.OlmManager
+import java.util.concurrent.atomic.AtomicBoolean
+import javax.inject.Inject
+
+data class MatrixConfiguration(
+ val applicationFlavor: String = "Default-application-flavor"
+) {
+
+ interface Provider {
+ fun providesMatrixConfiguration(): MatrixConfiguration
+ }
+
+}
+
+/**
+ * This is the main entry point to the matrix sdk.
+ * To get the singleton instance, use getInstance static method.
+ */
+class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
+
+ @Inject internal lateinit var authenticator: Authenticator
+ @Inject internal lateinit var userAgentHolder: UserAgentHolder
+ @Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
+ @Inject internal lateinit var olmManager: OlmManager
+ @Inject internal lateinit var sessionManager: SessionManager
+
+ init {
+ Monarchy.init(context)
+ DaggerMatrixComponent.factory().create(context).inject(this)
+ if (context.applicationContext !is Configuration.Provider) {
+ WorkManager.initialize(context, Configuration.Builder().build())
+ }
+ ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
+ userAgentHolder.setApplicationFlavor(matrixConfiguration.applicationFlavor)
+ }
+
+ fun getUserAgent() = userAgentHolder.userAgent
+
+ fun authenticator(): Authenticator {
+ return authenticator
+ }
+
+ companion object {
+
+ private lateinit var instance: Matrix
+ private val isInit = AtomicBoolean(false)
+
+ fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
+ if (isInit.compareAndSet(false, true)) {
+ instance = Matrix(context.applicationContext, matrixConfiguration)
+ }
+ }
+
+ fun getInstance(context: Context): Matrix {
+ if (isInit.compareAndSet(false, true)) {
+ val appContext = context.applicationContext
+ if (appContext is MatrixConfiguration.Provider) {
+ val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration()
+ instance = Matrix(appContext, matrixConfiguration)
+ } else {
+ throw IllegalStateException("Matrix is not initialized properly." +
+ " You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.")
+ }
+ }
+ return instance
+ }
+
+ fun getSdkVersion(): String {
+ return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
+ }
+ }
+
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt
new file mode 100644
index 0000000000..00d22b1f9f
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api
+
+/**
+ * Generic callback interface for asynchronously.
+ * @param the type of data to return on success
+ */
+interface MatrixCallback {
+
+ /**
+ * On success method, default to no-op
+ * @param data the data successfully returned from the async function
+ */
+ fun onSuccess(data: T) {
+ //no-op
+ }
+
+ /**
+ * On failure method, default to no-op
+ * @param failure the failure data returned from the async function
+ */
+ fun onFailure(failure: Throwable) {
+ //no-op
+ }
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt
new file mode 100644
index 0000000000..5cb7f4ca0c
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api
+
+
+/**
+ * This class contains pattern to match the different Matrix ids
+ */
+object MatrixPatterns {
+
+ // Note: TLD is not mandatory (localhost, IP address...)
+ private const val DOMAIN_REGEX = ":[A-Z0-9.-]+(:[0-9]{2,5})?"
+
+ // regex pattern to find matrix user ids in a string.
+ // See https://matrix.org/speculator/spec/HEAD/appendices.html#historical-user-ids
+ private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
+ private val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
+
+ // regex pattern to find room ids in a string.
+ private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
+ private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = MATRIX_ROOM_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
+
+ // regex pattern to find room aliases in a string.
+ private const val MATRIX_ROOM_ALIAS_REGEX = "#[A-Z0-9._%#@=+-]+$DOMAIN_REGEX"
+ private val PATTERN_CONTAIN_MATRIX_ALIAS = MATRIX_ROOM_ALIAS_REGEX.toRegex(RegexOption.IGNORE_CASE)
+
+ // regex pattern to find message ids in a string.
+ private const val MATRIX_EVENT_IDENTIFIER_REGEX = "\\$[A-Z0-9]+$DOMAIN_REGEX"
+ private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = MATRIX_EVENT_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
+
+ // regex pattern to find message ids in a string.
+ private const val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$[A-Z0-9/+]+"
+ private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 = MATRIX_EVENT_IDENTIFIER_V3_REGEX.toRegex(RegexOption.IGNORE_CASE)
+
+ // Ref: https://matrix.org/docs/spec/rooms/v4#event-ids
+ private const val MATRIX_EVENT_IDENTIFIER_V4_REGEX = "\\$[A-Z0-9\\-_]+"
+ private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4 = MATRIX_EVENT_IDENTIFIER_V4_REGEX.toRegex(RegexOption.IGNORE_CASE)
+
+ // regex pattern to find group ids in a string.
+ private const val MATRIX_GROUP_IDENTIFIER_REGEX = "\\+[A-Z0-9=_\\-./]+$DOMAIN_REGEX"
+ private val PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER = MATRIX_GROUP_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
+
+ // regex pattern to find permalink with message id.
+ // Android does not support in URL so extract it.
+ private const val PERMALINK_BASE_REGEX = "https://matrix\\.to/#/"
+ private const val APP_BASE_REGEX = "https://[A-Z0-9.-]+\\.[A-Z]{2,}/[A-Z]{3,}/#/room/"
+ const val SEP_REGEX = "/"
+
+ private const val LINK_TO_ROOM_ID_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
+ private val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ID = LINK_TO_ROOM_ID_REGEXP.toRegex(RegexOption.IGNORE_CASE)
+
+ private const val LINK_TO_ROOM_ALIAS_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
+ private val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ALIAS = LINK_TO_ROOM_ALIAS_REGEXP.toRegex(RegexOption.IGNORE_CASE)
+
+ private const val LINK_TO_APP_ROOM_ID_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
+ private val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ID = LINK_TO_APP_ROOM_ID_REGEXP.toRegex(RegexOption.IGNORE_CASE)
+
+ private const val LINK_TO_APP_ROOM_ALIAS_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
+ private val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ALIAS = LINK_TO_APP_ROOM_ALIAS_REGEXP.toRegex(RegexOption.IGNORE_CASE)
+
+ // list of patterns to find some matrix item.
+ val MATRIX_PATTERNS = listOf(
+ PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ID,
+ PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ALIAS,
+ PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ID,
+ PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ALIAS,
+ PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER,
+ PATTERN_CONTAIN_MATRIX_ALIAS,
+ PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER,
+ PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER,
+ PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
+ )
+
+ /**
+ * Tells if a string is a valid user Id.
+ *
+ * @param str the string to test
+ * @return true if the string is a valid user id
+ */
+ fun isUserId(str: String?): Boolean {
+ return str != null && str matches PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER
+ }
+
+ /**
+ * Tells if a string is a valid room id.
+ *
+ * @param str the string to test
+ * @return true if the string is a valid room Id
+ */
+ fun isRoomId(str: String?): Boolean {
+ return str != null && str matches PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER
+ }
+
+ /**
+ * Tells if a string is a valid room alias.
+ *
+ * @param str the string to test
+ * @return true if the string is a valid room alias.
+ */
+ fun isRoomAlias(str: String?): Boolean {
+ return str != null && str matches PATTERN_CONTAIN_MATRIX_ALIAS
+ }
+
+ /**
+ * Tells if a string is a valid event id.
+ *
+ * @param str the string to test
+ * @return true if the string is a valid event id.
+ */
+ fun isEventId(str: String?): Boolean {
+ return str != null
+ && (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER
+ || str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3
+ || str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
+ }
+
+ /**
+ * Tells if a string is a valid group id.
+ *
+ * @param str the string to test
+ * @return true if the string is a valid group id.
+ */
+ fun isGroupId(str: String?): Boolean {
+ return str != null && str matches PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt
new file mode 100644
index 0000000000..932ffbead9
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.auth
+
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
+import im.vector.matrix.android.api.auth.data.SessionParams
+import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.api.util.Cancelable
+
+/**
+ * This interface defines methods to authenticate to a matrix server.
+ */
+interface Authenticator {
+
+ /**
+ * @param homeServerConnectionConfig this param is used to configure the Homeserver
+ * @param login the login field
+ * @param password the password field
+ * @param callback the matrix callback on which you'll receive the result of authentication.
+ * @return return a [Cancelable]
+ */
+ fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig, login: String, password: String, callback: MatrixCallback): Cancelable
+
+ /**
+ * Check if there is an authenticated [Session].
+ * @return true if there is at least one active session.
+ */
+ fun hasAuthenticatedSessions(): Boolean
+
+ /**
+ * Get the last authenticated [Session], if there is an active session.
+ * @return the last active session if any, or null
+ */
+ fun getLastAuthenticatedSession(): Session?
+
+ /**
+ * Get an authenticated session. You should at least call authenticate one time before.
+ * If you logout, this session will no longer be valid.
+ *
+ * @param sessionParams the sessionParams to open with.
+ * @return the associated session if any, or null
+ */
+ fun getSession(sessionParams: SessionParams): Session?
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/Credentials.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/Credentials.kt
new file mode 100644
index 0000000000..d5962e261b
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/Credentials.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.auth.data
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+/**
+ * This data class hold credentials user data.
+ * You shouldn't have to instantiate it.
+ * The access token should be use to authenticate user in all server requests.
+ */
+@JsonClass(generateAdapter = true)
+data class Credentials(
+ @Json(name = "user_id") val userId: String,
+ @Json(name = "home_server") val homeServer: String,
+ @Json(name = "access_token") val accessToken: String,
+ @Json(name = "refresh_token") val refreshToken: String?,
+ @Json(name = "device_id") val deviceId: String?)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/HomeServerConnectionConfig.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/HomeServerConnectionConfig.kt
new file mode 100644
index 0000000000..f231d3f1bd
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/HomeServerConnectionConfig.kt
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.auth.data
+
+import android.net.Uri
+import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig.Builder
+import im.vector.matrix.android.internal.network.ssl.Fingerprint
+import okhttp3.CipherSuite
+import okhttp3.TlsVersion
+
+/**
+ * This data class holds how to connect to a specific Homeserver.
+ * It's used with [im.vector.matrix.android.api.auth.Authenticator] class.
+ * You should use the [Builder] to create one.
+ */
+@JsonClass(generateAdapter = true)
+data class HomeServerConnectionConfig(
+ val homeServerUri: Uri,
+ val identityServerUri: Uri,
+ val antiVirusServerUri: Uri? = null,
+ val allowedFingerprints: MutableList = ArrayList(),
+ val shouldPin: Boolean = false,
+ val tlsVersions: MutableList? = null,
+ val tlsCipherSuites: MutableList? = null,
+ val shouldAcceptTlsExtensions: Boolean = true,
+ val allowHttpExtension: Boolean = false,
+ val forceUsageTlsVersions: Boolean = false
+) {
+
+ /**
+ * This builder should be use to create a [HomeServerConnectionConfig] instance.
+ */
+ class Builder {
+
+ private lateinit var homeServerUri: Uri
+ private lateinit var identityServerUri: Uri
+ private var antiVirusServerUri: Uri? = null
+ private val allowedFingerprints: MutableList = ArrayList()
+ private var shouldPin: Boolean = false
+ private val tlsVersions: MutableList = ArrayList()
+ private val tlsCipherSuites: MutableList = ArrayList()
+ private var shouldAcceptTlsExtensions: Boolean = true
+ private var allowHttpExtension: Boolean = false
+ private var forceUsageTlsVersions: Boolean = false
+
+ fun withHomeServerUri(hsUriString: String): Builder {
+ return withHomeServerUri(Uri.parse(hsUriString))
+ }
+
+ /**
+ * @param hsUri The URI to use to connect to the homeserver.
+ * @return this builder
+ */
+ fun withHomeServerUri(hsUri: Uri): Builder {
+ if (hsUri.scheme != "http" && hsUri.scheme != "https") {
+ throw RuntimeException("Invalid home server URI: " + hsUri)
+ }
+ // ensure trailing /
+ homeServerUri = if (!hsUri.toString().endsWith("/")) {
+ try {
+ val url = hsUri.toString()
+ Uri.parse("$url/")
+ } catch (e: Exception) {
+ throw RuntimeException("Invalid home server URI: $hsUri")
+ }
+ } else {
+ hsUri
+ }
+ return this
+ }
+
+ fun withIdentityServerUri(identityServerUriString: String): Builder {
+ return withIdentityServerUri(Uri.parse(identityServerUriString))
+ }
+
+ /**
+ * @param identityServerUri The URI to use to manage identity.
+ * @return this builder
+ */
+ fun withIdentityServerUri(identityServerUri: Uri): Builder {
+ if (identityServerUri.scheme != "http" && identityServerUri.scheme != "https") {
+ throw RuntimeException("Invalid identity server URI: $identityServerUri")
+ }
+ // ensure trailing /
+ if (!identityServerUri.toString().endsWith("/")) {
+ try {
+ val url = identityServerUri.toString()
+ this.identityServerUri = Uri.parse("$url/")
+ } catch (e: Exception) {
+ throw RuntimeException("Invalid identity server URI: $identityServerUri")
+ }
+ } else {
+ this.identityServerUri = identityServerUri
+ }
+ return this
+ }
+
+ /**
+ * @param allowedFingerprints If using SSL, allow server certs that match these fingerprints.
+ * @return this builder
+ */
+ fun withAllowedFingerPrints(allowedFingerprints: List?): Builder {
+ if (allowedFingerprints != null) {
+ this.allowedFingerprints.addAll(allowedFingerprints)
+ }
+ return this
+ }
+
+ /**
+ * @param pin If true only allow certs matching given fingerprints, otherwise fallback to
+ * standard X509 checks.
+ * @return this builder
+ */
+ fun withPin(pin: Boolean): Builder {
+ this.shouldPin = pin
+ return this
+ }
+
+ /**
+ * @param shouldAcceptTlsExtension
+ * @return this builder
+ */
+ fun withShouldAcceptTlsExtensions(shouldAcceptTlsExtension: Boolean): Builder {
+ this.shouldAcceptTlsExtensions = shouldAcceptTlsExtension
+ return this
+ }
+
+ /**
+ * Add an accepted TLS version for TLS connections with the home server.
+ *
+ * @param tlsVersion the tls version to add to the set of TLS versions accepted.
+ * @return this builder
+ */
+ fun addAcceptedTlsVersion(tlsVersion: TlsVersion): Builder {
+ this.tlsVersions.add(tlsVersion)
+ return this
+ }
+
+ /**
+ * Force the usage of TlsVersion. This can be usefull for device on Android version < 20
+ *
+ * @param forceUsageOfTlsVersions set to true to force the usage of specified TlsVersions (with [.addAcceptedTlsVersion]
+ * @return this builder
+ */
+ fun forceUsageOfTlsVersions(forceUsageOfTlsVersions: Boolean): Builder {
+ this.forceUsageTlsVersions = forceUsageOfTlsVersions
+ return this
+ }
+
+ /**
+ * Add a TLS cipher suite to the list of accepted TLS connections with the home server.
+ *
+ * @param tlsCipherSuite the tls cipher suite to add.
+ * @return this builder
+ */
+ fun addAcceptedTlsCipherSuite(tlsCipherSuite: CipherSuite): Builder {
+ this.tlsCipherSuites.add(tlsCipherSuite)
+ return this
+ }
+
+ fun withAntiVirusServerUri(antivirusServerUriString: String?): Builder {
+ return withAntiVirusServerUri(antivirusServerUriString?.let { Uri.parse(it) })
+ }
+
+ /**
+ * Update the anti-virus server URI.
+ *
+ * @param antivirusServerUri the new anti-virus uri. Can be null
+ * @return this builder
+ */
+ fun withAntiVirusServerUri(antivirusServerUri: Uri?): Builder {
+ if (null != antivirusServerUri && "http" != antivirusServerUri.scheme && "https" != antivirusServerUri.scheme) {
+ throw RuntimeException("Invalid antivirus server URI: $antivirusServerUri")
+ }
+ this.antiVirusServerUri = antivirusServerUri
+ return this
+ }
+
+ /**
+ * Convenient method to limit the TLS versions and cipher suites for this Builder
+ * Ref:
+ * - https://www.ssi.gouv.fr/uploads/2017/02/security-recommendations-for-tls_v1.1.pdf
+ * - https://developer.android.com/reference/javax/net/ssl/SSLEngine
+ *
+ * @param tlsLimitations true to use Tls limitations
+ * @param enableCompatibilityMode set to true for Android < 20
+ * @return this builder
+ */
+ fun withTlsLimitations(tlsLimitations: Boolean, enableCompatibilityMode: Boolean): Builder {
+ if (tlsLimitations) {
+ withShouldAcceptTlsExtensions(false)
+
+ // Tls versions
+ addAcceptedTlsVersion(TlsVersion.TLS_1_2)
+ addAcceptedTlsVersion(TlsVersion.TLS_1_3)
+
+ forceUsageOfTlsVersions(enableCompatibilityMode)
+
+ // Cipher suites
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
+
+ if (enableCompatibilityMode) {
+ // Adopt some preceding cipher suites for Android < 20 to be able to negotiate
+ // a TLS session.
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
+ addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
+ }
+ }
+ return this
+ }
+
+ fun withAllowHttpConnection(allowHttpExtension: Boolean): Builder {
+ this.allowHttpExtension = allowHttpExtension
+ return this
+ }
+
+ /**
+ * @return the [HomeServerConnectionConfig]
+ */
+ fun build(): HomeServerConnectionConfig {
+ return HomeServerConnectionConfig(
+ homeServerUri,
+ identityServerUri,
+ antiVirusServerUri,
+ allowedFingerprints,
+ shouldPin,
+ tlsVersions,
+ tlsCipherSuites,
+ shouldAcceptTlsExtensions,
+ allowHttpExtension,
+ forceUsageTlsVersions
+ )
+ }
+
+ }
+
+
+}
+
+
+
+
+
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/SessionParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/SessionParams.kt
new file mode 100644
index 0000000000..a104f2c031
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/SessionParams.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.auth.data
+
+/**
+ * This data class holds necessary data to open a session.
+ * You don't have to manually instantiate it.
+ */
+data class SessionParams(
+ val credentials: Credentials,
+ val homeServerConnectionConfig: HomeServerConnectionConfig
+)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/comparators/DatedObjectComparators.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/comparators/DatedObjectComparators.kt
new file mode 100644
index 0000000000..aad33ab7b9
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/comparators/DatedObjectComparators.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.comparators
+
+import im.vector.matrix.android.api.interfaces.DatedObject
+import java.util.*
+
+object DatedObjectComparators {
+
+ /**
+ * Comparator to sort DatedObjects from the oldest to the latest.
+ */
+ val ascComparator by lazy {
+ Comparator { datedObject1, datedObject2 ->
+ (datedObject1.date - datedObject2.date).toInt()
+ }
+ }
+
+ /**
+ * Comparator to sort DatedObjects from the latest to the oldest.
+ */
+ val descComparator by lazy {
+ Comparator { datedObject1, datedObject2 ->
+ (datedObject2.date - datedObject1.date).toInt()
+ }
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt
new file mode 100644
index 0000000000..e2e53904ff
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.extensions
+
+import im.vector.matrix.android.api.comparators.DatedObjectComparators
+import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
+import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
+import java.util.*
+
+/* ==========================================================================================
+ * MXDeviceInfo
+ * ========================================================================================== */
+
+fun MXDeviceInfo.getFingerprintHumanReadable() = fingerprint()
+ ?.chunked(4)
+ ?.joinToString(separator = " ")
+
+
+fun List.sortByLastSeen() {
+ Collections.sort(this, DatedObjectComparators.descComparator)
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt
new file mode 100644
index 0000000000..152adb0665
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.failure
+
+import im.vector.matrix.android.api.session.crypto.MXCryptoError
+import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
+import java.io.IOException
+
+/**
+ * This class allows to expose different kinds of error to be then handled by the application.
+ * As it is a sealed class, you typically use it like that :
+ * when(failure) {
+ * is NetworkConnection -> Unit
+ * is ServerError -> Unit
+ * is Unknown -> Unit
+ * }
+ */
+sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) {
+ data class Unknown(val throwable: Throwable? = null) : Failure(throwable)
+ data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException)
+ data class ServerError(val error: MatrixError, val httpCode: Int) : Failure(RuntimeException(error.toString()))
+ // When server send an error, but it cannot be interpreted as a MatrixError
+ data class OtherServerError(val errorBody: String, val httpCode: Int) : Failure(RuntimeException(errorBody))
+
+ data class RegistrationFlowError(val registrationFlowResponse: RegistrationFlowResponse) : Failure(RuntimeException(registrationFlowResponse.toString()))
+
+ data class CryptoError(val error: MXCryptoError) : Failure(error)
+
+ abstract class FeatureFailure : Failure()
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-core/src/main/java/im/vector/matrix/core/api/failure/MatrixError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/MatrixError.kt
similarity index 53%
rename from matrix-sdk-core/src/main/java/im/vector/matrix/core/api/failure/MatrixError.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/MatrixError.kt
index d8cfa15571..1e87cfc1b2 100644
--- a/matrix-sdk-core/src/main/java/im/vector/matrix/core/api/failure/MatrixError.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/MatrixError.kt
@@ -1,14 +1,39 @@
-package im.vector.matrix.core.api.failure
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.failure
import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
-data class MatrixError(@Json(name = "errcode") val code: String,
- @Json(name = "error") val message: String) {
+/**
+ * This data class holds the error defined by the matrix specifications.
+ * You shouldn't have to instantiate it.
+ */
+@JsonClass(generateAdapter = true)
+data class MatrixError(
+ @Json(name = "errcode") val code: String,
+ @Json(name = "error") val message: String
+) {
companion object {
const val FORBIDDEN = "M_FORBIDDEN"
const val UNKNOWN = "M_UNKNOWN"
const val UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN"
+ const val MISSING_TOKEN = "M_MISSING_TOKEN"
const val BAD_JSON = "M_BAD_JSON"
const val NOT_JSON = "M_NOT_JSON"
const val NOT_FOUND = "M_NOT_FOUND"
@@ -29,5 +54,6 @@ data class MatrixError(@Json(name = "errcode") val code: String,
const val TOO_LARGE = "M_TOO_LARGE"
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
const val RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
+ const val WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
}
}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/interfaces/DatedObject.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/interfaces/DatedObject.kt
new file mode 100644
index 0000000000..cd73550692
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/interfaces/DatedObject.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.interfaces
+
+/**
+ * Can be implemented by any object containing a timestamp.
+ * This interface can be use to sort such object
+ */
+interface DatedObject {
+ val date: Long
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt
new file mode 100644
index 0000000000..854f0e9fb6
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.listeners
+
+/**
+ * Interface to send a progress info
+ */
+interface ProgressListener {
+ /**
+ * @param progress from 0 to total by contract
+ * @param total
+ */
+ fun onProgress(progress: Int, total: Int)
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/StepProgressListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/StepProgressListener.kt
new file mode 100644
index 0000000000..af5b815c1a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/StepProgressListener.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.listeners
+
+/**
+ * Interface to send a progress info
+ */
+interface StepProgressListener {
+
+ sealed class Step {
+ data class ComputingKey(val progress: Int, val total: Int) : Step()
+ object DownloadingKey : Step()
+ data class ImportingKey(val progress: Int, val total: Int) : Step()
+ }
+
+ /**
+ * @param step The current step, containing progress data if available. Else you should consider progress as indeterminate
+ */
+ fun onStepProgress(step: Step)
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixLinkify.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixLinkify.kt
new file mode 100644
index 0000000000..cfe5a051e7
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixLinkify.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.permalinks
+
+import android.text.Spannable
+import im.vector.matrix.android.api.MatrixPatterns
+
+/**
+ * MatrixLinkify take a piece of text and turns all of the
+ * matrix patterns matches in the text into clickable links.
+ */
+object MatrixLinkify {
+
+ /**
+ * Find the matrix spans i.e matrix id , user id ... to display them as URL.
+ *
+ * @param spannable the text in which the matrix items has to be clickable.
+ */
+ fun addLinks(spannable: Spannable?, callback: MatrixPermalinkSpan.Callback?): Boolean {
+ // sanity checks
+ if (spannable.isNullOrEmpty()) {
+ return false
+ }
+ val text = spannable.toString()
+ var hasMatch = false
+ for (pattern in MatrixPatterns.MATRIX_PATTERNS) {
+ for (match in pattern.findAll(spannable)) {
+ hasMatch = true
+ val startPos = match.range.first
+ if (startPos == 0 || text[startPos - 1] != '/') {
+ val endPos = match.range.last
+ val url = text.substring(match.range)
+ val span = MatrixPermalinkSpan(url, callback)
+ spannable.setSpan(span, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
+ }
+ }
+ }
+ return hasMatch
+ }
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixPermalinkSpan.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixPermalinkSpan.kt
new file mode 100644
index 0000000000..bbef1d36b1
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixPermalinkSpan.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.permalinks
+
+import android.text.style.ClickableSpan
+import android.view.View
+
+/**
+ * This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back.
+ * @param url the permalink url tied to the span
+ * @param callback the callback to use.
+ */
+class MatrixPermalinkSpan(private val url: String,
+ private val callback: Callback? = null) : ClickableSpan() {
+
+ interface Callback {
+ fun onUrlClicked(url: String)
+ }
+
+ override fun onClick(widget: View) {
+ callback?.onUrlClicked(url)
+ }
+
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkData.kt
new file mode 100644
index 0000000000..615892a107
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkData.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.permalinks
+
+import android.net.Uri
+
+/**
+ * This sealed class represents all the permalink cases.
+ * You don't have to instantiate yourself but should use [PermalinkParser] instead.
+ */
+sealed class PermalinkData {
+
+ data class EventLink(val roomIdOrAlias: String, val eventId: String) : PermalinkData()
+
+ data class RoomLink(val roomIdOrAlias: String) : PermalinkData()
+
+ data class UserLink(val userId: String) : PermalinkData()
+
+ data class GroupLink(val groupId: String) : PermalinkData()
+
+ data class FallbackLink(val uri: Uri) : PermalinkData()
+
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt
new file mode 100644
index 0000000000..15d56eacb3
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.permalinks
+
+import android.text.TextUtils
+import im.vector.matrix.android.api.session.events.model.Event
+
+/**
+ * Useful methods to create Matrix permalink.
+ */
+object PermalinkFactory {
+
+ const val MATRIX_TO_URL_BASE = "https://matrix.to/#/"
+
+ /**
+ * Creates a permalink for an event.
+ * Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org"
+ *
+ * @param event the event
+ * @return the permalink, or null in case of error
+ */
+ fun createPermalink(event: Event): String? {
+ if (event.roomId.isNullOrEmpty() || event.eventId.isNullOrEmpty()) {
+ return null
+ }
+ return createPermalink(event.roomId, event.eventId)
+ }
+
+ /**
+ * Creates a permalink for an id (can be a user Id, Room Id, etc.).
+ * Ex: "https://matrix.to/#/@benoit:matrix.org"
+ *
+ * @param id the id
+ * @return the permalink, or null in case of error
+ */
+ fun createPermalink(id: String): String? {
+ return if (TextUtils.isEmpty(id)) {
+ null
+ } else MATRIX_TO_URL_BASE + escape(id)
+
+ }
+
+ /**
+ * Creates a permalink for an event. If you have an event you can use [.createPermalink]
+ * Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org"
+ *
+ * @param roomId the id of the room
+ * @param eventId the id of the event
+ * @return the permalink
+ */
+ fun createPermalink(roomId: String, eventId: String): String {
+ return MATRIX_TO_URL_BASE + escape(roomId) + "/" + escape(eventId)
+ }
+
+ /**
+ * Extract the linked id from the universal link
+ *
+ * @param url the universal link, Ex: "https://matrix.to/#/@benoit:matrix.org"
+ * @return the id from the url, ex: "@benoit:matrix.org", or null if the url is not a permalink
+ */
+ fun getLinkedId(url: String?): String? {
+ val isSupported = url != null && url.startsWith(MATRIX_TO_URL_BASE)
+
+ return if (isSupported) {
+ url!!.substring(MATRIX_TO_URL_BASE.length)
+ } else null
+
+ }
+
+
+ /**
+ * Escape '/' in id, because it is used as a separator
+ *
+ * @param id the id to escape
+ * @return the escaped id
+ */
+ private fun escape(id: String): String {
+ return id.replace("/".toRegex(), "%2F")
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkParser.kt
new file mode 100644
index 0000000000..71fd16e778
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkParser.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.permalinks
+
+import android.net.Uri
+import im.vector.matrix.android.api.MatrixPatterns
+
+/**
+ * This class turns an uri to a [PermalinkData]
+ */
+object PermalinkParser {
+
+ /**
+ * Turns an uri string to a [PermalinkData]
+ */
+ fun parse(uriString: String): PermalinkData {
+ val uri = Uri.parse(uriString)
+ return parse(uri)
+ }
+
+ /**
+ * Turns an uri to a [PermalinkData]
+ */
+ fun parse(uri: Uri): PermalinkData {
+ if (!uri.toString().startsWith(PermalinkFactory.MATRIX_TO_URL_BASE)) {
+ return PermalinkData.FallbackLink(uri)
+ }
+
+ val fragment = uri.fragment
+ if (fragment.isNullOrEmpty()) {
+ return PermalinkData.FallbackLink(uri)
+ }
+
+ val indexOfQuery = fragment.indexOf("?")
+ val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment
+
+ // we are limiting to 2 params
+ val params = safeFragment
+ .split(MatrixPatterns.SEP_REGEX.toRegex())
+ .filter { it.isNotEmpty() }
+ .take(2)
+
+ val identifier = params.getOrNull(0)
+ val extraParameter = params.getOrNull(1)
+ if (identifier.isNullOrEmpty()) {
+ return PermalinkData.FallbackLink(uri)
+ }
+ return when {
+ MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
+ MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
+ MatrixPatterns.isRoomId(identifier) -> {
+ if (!extraParameter.isNullOrEmpty() && MatrixPatterns.isEventId(extraParameter)) {
+ PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter)
+ } else {
+ PermalinkData.RoomLink(roomIdOrAlias = identifier)
+ }
+ }
+ else -> PermalinkData.FallbackLink(uri)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/Action.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/Action.kt
new file mode 100644
index 0000000000..797830f491
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/Action.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+import im.vector.matrix.android.api.pushrules.rest.PushRule
+import timber.log.Timber
+
+
+class Action(val type: Type) {
+
+ enum class Type(val value: String) {
+ NOTIFY("notify"),
+ DONT_NOTIFY("dont_notify"),
+ COALESCE("coalesce"),
+ SET_TWEAK("set_tweak");
+
+ companion object {
+
+ fun safeValueOf(value: String): Type? {
+ try {
+ return valueOf(value)
+ } catch (e: IllegalArgumentException) {
+ return null
+ }
+ }
+ }
+ }
+
+ var tweak_action: String? = null
+ var stringValue: String? = null
+ var boolValue: Boolean? = null
+
+ companion object {
+ fun mapFrom(pushRule: PushRule): List? {
+ val actions = ArrayList()
+ pushRule.actions.forEach { actionStrOrObj ->
+ if (actionStrOrObj is String) {
+ when (actionStrOrObj) {
+ Action.Type.NOTIFY.value -> Action(Action.Type.NOTIFY)
+ Action.Type.DONT_NOTIFY.value -> Action(Action.Type.DONT_NOTIFY)
+ else -> {
+ Timber.w("Unsupported action type ${actionStrOrObj}")
+ null
+ }
+ }?.let {
+ actions.add(it)
+ }
+ } else if (actionStrOrObj is Map<*, *>) {
+ val tweakAction = actionStrOrObj["set_tweak"] as? String
+ when (tweakAction) {
+ "sound" -> {
+ (actionStrOrObj["value"] as? String)?.let { stringValue ->
+ Action(Action.Type.SET_TWEAK).also {
+ it.tweak_action = "sound"
+ it.stringValue = stringValue
+ actions.add(it)
+ }
+ }
+ }
+ "highlight" -> {
+ (actionStrOrObj["value"] as? Boolean)?.let { boolValue ->
+ Action(Action.Type.SET_TWEAK).also {
+ it.tweak_action = "highlight"
+ it.boolValue = boolValue
+ actions.add(it)
+ }
+ }
+ }
+ else -> {
+ Timber.w("Unsupported action type ${actionStrOrObj}")
+ }
+ }
+ } else {
+ Timber.w("Unsupported action type ${actionStrOrObj}")
+ return null
+ }
+ }
+ return if (actions.isEmpty()) null else actions
+ }
+ }
+}
+
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/Condition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/Condition.kt
new file mode 100644
index 0000000000..c0bb4f16f4
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/Condition.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+abstract class Condition(val kind: Kind) {
+
+ enum class Kind(val value: String) {
+ event_match("event_match"),
+ contains_display_name("contains_display_name"),
+ room_member_count("room_member_count"),
+ sender_notification_permission("sender_notification_permission"),
+ UNRECOGNIZE("");
+
+ companion object {
+
+ fun fromString(value: String): Kind {
+ return when (value) {
+ "event_match" -> event_match
+ "contains_display_name" -> contains_display_name
+ "room_member_count" -> room_member_count
+ "sender_notification_permission" -> sender_notification_permission
+ else -> UNRECOGNIZE
+ }
+ }
+
+ }
+
+ }
+
+ abstract fun isSatisfied(conditionResolver: ConditionResolver): Boolean
+
+ open fun technicalDescription(): String {
+ return "Kind: $kind"
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ConditionResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ConditionResolver.kt
new file mode 100644
index 0000000000..4d15d5deec
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ConditionResolver.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+/**
+ * Acts like a visitor on Conditions.
+ * This class as all required context needed to evaluate rules
+ */
+interface ConditionResolver {
+
+ fun resolveEventMatchCondition(eventMatchCondition: EventMatchCondition): Boolean
+ fun resolveRoomMemberCountCondition(roomMemberCountCondition: RoomMemberCountCondition): Boolean
+ fun resolveSenderNotificationPermissionCondition(senderNotificationPermissionCondition: SenderNotificationPermissionCondition): Boolean
+ fun resolveContainsDisplayNameCondition(containsDisplayNameCondition: ContainsDisplayNameCondition) : Boolean
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ContainsDisplayNameCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ContainsDisplayNameCondition.kt
new file mode 100644
index 0000000000..37bc75ad57
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ContainsDisplayNameCondition.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+import android.text.TextUtils
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.events.model.EventType
+import im.vector.matrix.android.api.session.events.model.toModel
+import im.vector.matrix.android.api.session.room.model.message.MessageContent
+import timber.log.Timber
+import java.util.regex.Pattern
+
+class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
+
+ override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
+ return conditionResolver.resolveContainsDisplayNameCondition(this)
+ }
+
+ override fun technicalDescription(): String {
+ return "User is mentioned"
+ }
+
+ fun isSatisfied(event: Event, displayName: String): Boolean {
+ var message = when (event.type) {
+ EventType.MESSAGE -> {
+ event.content.toModel()
+ }
+ //TODO the spec says:
+ // Matches any message whose content is unencrypted and contains the user's current display name
+ // EventType.ENCRYPTED -> {
+ // event.root.getClearContent()?.toModel()
+ // }
+ else -> null
+ } ?: return false
+
+ return caseInsensitiveFind(displayName, message.body)
+ }
+
+
+ companion object {
+ /**
+ * Returns whether a string contains an occurrence of another, as a standalone word, regardless of case.
+ *
+ * @param subString the string to search for
+ * @param longString the string to search in
+ * @return whether a match was found
+ */
+ fun caseInsensitiveFind(subString: String, longString: String): Boolean {
+ // add sanity checks
+ if (TextUtils.isEmpty(subString) || TextUtils.isEmpty(longString)) {
+ return false
+ }
+
+ var res = false
+
+ try {
+ val pattern = Pattern.compile("(\\W|^)" + Pattern.quote(subString) + "(\\W|$)", Pattern.CASE_INSENSITIVE)
+ res = pattern.matcher(longString).find()
+ } catch (e: Exception) {
+ Timber.e(e, "## caseInsensitiveFind() : failed")
+ }
+
+ return res
+ }
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/EventMatchCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/EventMatchCondition.kt
new file mode 100644
index 0000000000..7325abba2a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/EventMatchCondition.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.internal.di.MoshiProvider
+import timber.log.Timber
+
+class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind.event_match) {
+
+ override fun isSatisfied(conditionResolver: ConditionResolver) : Boolean {
+ return conditionResolver.resolveEventMatchCondition(this)
+ }
+
+ override fun technicalDescription(): String {
+ return "'$key' Matches '$pattern'"
+ }
+
+
+ fun isSatisfied(event: Event): Boolean {
+ //TODO encrypted events?
+ val rawJson = MoshiProvider.providesMoshi().adapter(Event::class.java).toJsonValue(event) as? Map<*, *>
+ ?: return false
+ val value = extractField(rawJson, key) ?: return false
+
+ //Patterns with no special glob characters should be treated as having asterisks prepended
+ // and appended when testing the condition.
+ try {
+ val modPattern = if (hasSpecialGlobChar(pattern)) simpleGlobToRegExp(pattern) else simpleGlobToRegExp("*$pattern*")
+ val regex = Regex(modPattern, RegexOption.DOT_MATCHES_ALL)
+ return regex.containsMatchIn(value)
+ } catch (e: Throwable) {
+ //e.g PatternSyntaxException
+ Timber.e(e, "Failed to evaluate push condition")
+ return false
+ }
+
+ }
+
+
+ private fun extractField(jsonObject: Map<*, *>, fieldPath: String): String? {
+ val fieldParts = fieldPath.split(".")
+ if (fieldParts.isEmpty()) return null
+
+ var jsonElement: Map<*, *> = jsonObject
+ fieldParts.forEachIndexed { index, pathSegment ->
+ if (index == fieldParts.lastIndex) {
+ return jsonElement[pathSegment]?.toString()
+ } else {
+ val sub = jsonElement[pathSegment] ?: return null
+ if (sub is Map<*, *>) {
+ jsonElement = sub
+ } else {
+ return null
+ }
+ }
+ }
+ return null
+ }
+
+ companion object {
+
+ private fun hasSpecialGlobChar(glob: String): Boolean {
+ return glob.contains("*") || glob.contains("?")
+ }
+
+ //Very simple glob to regexp converter
+ private fun simpleGlobToRegExp(glob: String): String {
+ var out = ""//"^"
+ for (i in 0 until glob.length) {
+ val c = glob[i]
+ when (c) {
+ '*' -> out += ".*"
+ '?' -> out += '.'.toString()
+ '.' -> out += "\\."
+ '\\' -> out += "\\\\"
+ else -> out += c
+ }
+ }
+ out += ""//'$'.toString()
+ return out
+ }
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/PushRuleService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/PushRuleService.kt
new file mode 100644
index 0000000000..b00450b561
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/PushRuleService.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.pushrules.rest.PushRule
+import im.vector.matrix.android.api.session.events.model.Event
+
+interface PushRuleService {
+
+ /**
+ * Fetch the push rules from the server
+ */
+ fun fetchPushRules(scope: String = "global")
+
+ //TODO get push rule set
+ fun getPushRules(scope: String = "global"): List
+
+ //TODO update rule
+
+ fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback)
+
+ fun addPushRuleListener(listener: PushRuleListener)
+
+ fun removePushRuleListener(listener: PushRuleListener)
+
+// fun fulfilledBingRule(event: Event, rules: List): PushRule?
+
+ interface PushRuleListener {
+ fun onMatchRule(event: Event, actions: List)
+ fun batchFinish()
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RoomMemberCountCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RoomMemberCountCondition.kt
new file mode 100644
index 0000000000..b2bed4c230
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RoomMemberCountCondition.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.room.RoomService
+import timber.log.Timber
+import java.util.regex.Pattern
+
+private val regex = Pattern.compile("^(==|<=|>=|<|>)?(\\d*)$")
+
+class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_count) {
+
+ override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
+ return conditionResolver.resolveRoomMemberCountCondition(this)
+ }
+
+ override fun technicalDescription(): String {
+ return "Room member count is $`is`"
+ }
+
+ fun isSatisfied(event: Event, session: RoomService?): Boolean {
+ // sanity check^
+ val roomId = event.roomId ?: return false
+ val room = session?.getRoom(roomId) ?: return false
+
+ // Parse the is field into prefix and number the first time
+ val (prefix, count) = parseIsField() ?: return false
+
+ val numMembers = room.getNumberOfJoinedMembers()
+
+ return when (prefix) {
+ "<" -> numMembers < count
+ ">" -> numMembers > count
+ "<=" -> numMembers <= count
+ ">=" -> numMembers >= count
+ else -> numMembers == count
+ }
+ }
+
+ /**
+ * Parse the is field to extract meaningful information.
+ */
+ private fun parseIsField(): Pair? {
+ try {
+ val match = regex.matcher(`is`)
+ if (match.find()) {
+ val prefix = match.group(1)
+ val count = match.group(2).toInt()
+ return prefix to count
+ }
+ } catch (t: Throwable) {
+ Timber.d(t)
+ }
+ return null
+
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RuleIds.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RuleIds.kt
new file mode 100644
index 0000000000..38a64adf27
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RuleIds.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.pushrules
+
+/**
+ * Known rule ids
+ *
+ * Ref: https://matrix.org/docs/spec/client_server/latest#predefined-rules
+ */
+object RuleIds {
+ // Default Override Rules
+ const val RULE_ID_DISABLE_ALL = ".m.rule.master"
+ const val RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS = ".m.rule.suppress_notices"
+ const val RULE_ID_INVITE_ME = ".m.rule.invite_for_me"
+ const val RULE_ID_PEOPLE_JOIN_LEAVE = ".m.rule.member_event"
+ const val RULE_ID_CONTAIN_DISPLAY_NAME = ".m.rule.contains_display_name"
+
+ const val RULE_ID_TOMBSTONE = ".m.rule.tombstone"
+ const val RULE_ID_ROOM_NOTIF = ".m.rule.roomnotif"
+
+ // Default Content Rules
+ const val RULE_ID_CONTAIN_USER_NAME = ".m.rule.contains_user_name"
+
+ // Default Underride Rules
+ const val RULE_ID_CALL = ".m.rule.call"
+ const val RULE_ID_one_to_one_encrypted_room = ".m.rule.encrypted_room_one_to_one"
+ const val RULE_ID_ONE_TO_ONE_ROOM = ".m.rule.room_one_to_one"
+ const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message"
+ const val RULE_ID_ENCRYPTED = ".m.rule.encrypted"
+
+ // Not documented
+ const val RULE_ID_FALLBACK = ".m.rule.fallback"
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RulesetKey.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RulesetKey.kt
new file mode 100644
index 0000000000..834bdf2add
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/RulesetKey.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+
+enum class RulesetKey(val value: String) {
+ CONTENT("content"),
+ OVERRIDE("override"),
+ ROOM("room"),
+ SENDER("sender"),
+ UNDERRIDE("underride"),
+ UNKNOWN("")
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt
new file mode 100644
index 0000000000..35b731c5c3
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules
+
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.room.model.PowerLevels
+
+
+class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) {
+
+ override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
+ return conditionResolver.resolveSenderNotificationPermissionCondition(this)
+ }
+
+ override fun technicalDescription(): String {
+ return "User power level <$key>"
+ }
+
+
+ fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean {
+ return event.senderId != null && powerLevels.getUserPowerLevel(event.senderId) >= powerLevels.notificationLevel(key)
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/GetPushRulesResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/GetPushRulesResponse.kt
new file mode 100644
index 0000000000..8d567f9464
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/GetPushRulesResponse.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules.rest
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+/**
+ * All push rulesets for a user.
+ */
+@JsonClass(generateAdapter = true)
+data class GetPushRulesResponse(
+ /**
+ * Global rules, account level applying to all devices
+ */
+ @Json(name = "global")
+ val global: Ruleset,
+
+ /**
+ * Device specific rules, apply only to current device
+ */
+ @Json(name = "device")
+ val device: Ruleset? = null
+)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushCondition.kt
new file mode 100644
index 0000000000..8f69401653
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushCondition.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules.rest
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.api.pushrules.*
+import timber.log.Timber
+
+@JsonClass(generateAdapter = true)
+data class PushCondition(
+ /**
+ * Required. The kind of condition to apply.
+ */
+ val kind: String,
+
+ /**
+ * Required for event_match conditions. The dot- separated field of the event to match.
+ */
+
+ val key: String? = null,
+ /**
+ *Required for event_match conditions.
+ */
+
+ val pattern: String? = null,
+ /**
+ * Required for room_member_count conditions.
+ * A decimal integer optionally prefixed by one of, ==, <, >, >= or <=.
+ * A prefix of < matches rooms where the member count is strictly less than the given number and so forth.
+ * If no prefix is present, this parameter defaults to ==.
+ */
+ @Json(name = "is") val iz: String? = null
+) {
+
+ fun asExecutableCondition(): Condition? {
+ return when (Condition.Kind.fromString(this.kind)) {
+ Condition.Kind.event_match -> {
+ if (this.key != null && this.pattern != null) {
+ EventMatchCondition(key, pattern)
+ } else {
+ Timber.e("Malformed Event match condition")
+ null
+ }
+ }
+ Condition.Kind.contains_display_name -> {
+ ContainsDisplayNameCondition()
+ }
+ Condition.Kind.room_member_count -> {
+ if (this.iz.isNullOrBlank()) {
+ Timber.e("Malformed ROOM_MEMBER_COUNT condition")
+ null
+ } else {
+ RoomMemberCountCondition(this.iz)
+ }
+ }
+ Condition.Kind.sender_notification_permission -> {
+ this.key?.let { SenderNotificationPermissionCondition(it) }
+ }
+ Condition.Kind.UNRECOGNIZE -> {
+ Timber.e("Unknwon kind $kind")
+ null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushRule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushRule.kt
new file mode 100644
index 0000000000..1e36a0d867
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushRule.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.pushrules.rest
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+
+@JsonClass(generateAdapter = true)
+data class PushRule(
+ /**
+ * Required. The actions to perform when this rule is matched.
+ */
+ val actions: List,
+ /**
+ * Required. Whether this is a default rule, or has been set explicitly.
+ */
+ val default: Boolean? = false,
+ /**
+ * Required. Whether the push rule is enabled or not.
+ */
+ val enabled: Boolean,
+ /**
+ * Required. The ID of this rule.
+ */
+ @Json(name = "rule_id") val ruleId: String,
+ /**
+ * The conditions that must hold true for an event in order for a rule to be applied to an event
+ */
+ val conditions: List? = null,
+ /**
+ * The glob-style pattern to match against. Only applicable to content rules.
+ */
+ val pattern: String? = null
+)
+
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/Ruleset.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/Ruleset.kt
new file mode 100644
index 0000000000..5f4ca15ac0
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/Ruleset.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.pushrules.rest
+
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class Ruleset(
+ val content: List? = null,
+ val override: List? = null,
+ val room: List? = null,
+ val sender: List? = null,
+ val underride: List? = null
+)
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/InitialSyncProgressService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/InitialSyncProgressService.kt
new file mode 100644
index 0000000000..3bb567ae00
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/InitialSyncProgressService.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.session
+
+import androidx.annotation.StringRes
+import androidx.lifecycle.LiveData
+
+interface InitialSyncProgressService {
+
+ fun getLiveStatus() : LiveData
+
+ data class Status(
+ @StringRes val statusText: Int?,
+ val percentProgress: Int = 0
+ )
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
new file mode 100644
index 0000000000..08e706c3e4
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session
+
+import androidx.annotation.MainThread
+import androidx.lifecycle.LiveData
+import im.vector.matrix.android.api.auth.data.SessionParams
+import im.vector.matrix.android.api.pushrules.PushRuleService
+import im.vector.matrix.android.api.session.cache.CacheService
+import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
+import im.vector.matrix.android.api.session.content.ContentUrlResolver
+import im.vector.matrix.android.api.session.crypto.CryptoService
+import im.vector.matrix.android.api.session.file.FileService
+import im.vector.matrix.android.api.session.group.GroupService
+import im.vector.matrix.android.api.session.pushers.PushersService
+import im.vector.matrix.android.api.session.room.RoomDirectoryService
+import im.vector.matrix.android.api.session.room.RoomService
+import im.vector.matrix.android.api.session.signout.SignOutService
+import im.vector.matrix.android.api.session.sync.FilterService
+import im.vector.matrix.android.api.session.sync.SyncState
+import im.vector.matrix.android.api.session.user.UserService
+
+/**
+ * This interface defines interactions with a session.
+ * An instance of a session will be provided by the SDK.
+ */
+interface Session :
+ RoomService,
+ RoomDirectoryService,
+ GroupService,
+ UserService,
+ CryptoService,
+ CacheService,
+ SignOutService,
+ FilterService,
+ FileService,
+ PushRuleService,
+ PushersService,
+ InitialSyncProgressService {
+
+ /**
+ * The params associated to the session
+ */
+ val sessionParams: SessionParams
+
+ val myUserId: String
+ get() = sessionParams.credentials.userId
+
+
+ /**
+ * This method allow to open a session. It does start some service on the background.
+ */
+ @MainThread
+ fun open()
+
+ /**
+ * Requires a one time background sync
+ */
+ fun requireBackgroundSync()
+
+ /**
+ * Launches infinite periodic background syncs
+ * THis does not work in doze mode :/
+ * If battery optimization is on it can work in app standby but that's all :/
+ */
+ fun startAutomaticBackgroundSync(repeatDelay: Long = 30_000L)
+
+ fun stopAnyBackgroundSync()
+
+ /**
+ * This method start the sync thread.
+ */
+ fun startSync()
+
+ /**
+ * This method stop the sync thread.
+ */
+ fun stopSync()
+
+ /**
+ * This method allows to listen the sync state.
+ * @return a [LiveData] of [SyncState].
+ */
+ fun syncState(): LiveData
+
+ /**
+ * This method allow to close a session. It does stop some services.
+ */
+ fun close()
+
+ /**
+ * Returns the ContentUrlResolver associated to the session.
+ */
+ fun contentUrlResolver(): ContentUrlResolver
+
+ /**
+ * Returns the ContentUploadProgressTracker associated with the session
+ */
+ fun contentUploadProgressTracker(): ContentUploadStateTracker
+
+ /**
+ * Add a listener to the session.
+ * @param listener the listener to add.
+ */
+ fun addListener(listener: Listener)
+
+ /**
+ * Remove a listener from the session.
+ * @param listener the listener to remove.
+ */
+ fun removeListener(listener: Listener)
+
+ /**
+ * A global session listener to get notified for some events.
+ */
+ interface Listener {
+ /**
+ * The access token is not valid anymore
+ */
+ fun onInvalidToken()
+
+ }
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/cache/CacheService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/cache/CacheService.kt
new file mode 100644
index 0000000000..43ab78b5aa
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/cache/CacheService.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.cache
+
+import im.vector.matrix.android.api.MatrixCallback
+
+/**
+ * This interface defines a method to sign out. It's implemented at the session level.
+ */
+interface CacheService {
+
+ /**
+ * Clear the whole cached data, except credentials. Once done, the session is closed and has to be opened again
+ */
+ fun clearCache(callback: MatrixCallback)
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt
new file mode 100644
index 0000000000..c8dca8692c
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.content
+
+import android.os.Parcelable
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+data class ContentAttachmentData(
+ val size: Long = 0,
+ val duration: Long? = 0,
+ val date: Long = 0,
+ val height: Long? = 0,
+ val width: Long? = 0,
+ val name: String? = null,
+ val path: String,
+ val mimeType: String,
+ val type: Type
+) : Parcelable {
+
+ enum class Type {
+ FILE,
+ IMAGE,
+ AUDIO,
+ VIDEO
+ }
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt
new file mode 100644
index 0000000000..26273ebb8a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.content
+
+interface ContentUploadStateTracker {
+
+ fun track(key: String, updateListener: UpdateListener)
+
+ fun untrack(key: String, updateListener: UpdateListener)
+
+ interface UpdateListener {
+ fun onUpdate(state: State)
+ }
+
+ sealed class State {
+ object Idle : State()
+ object EncryptingThumbnail : State()
+ data class UploadingThumbnail(val current: Long, val total: Long) : State()
+ object Encrypting : State()
+ data class Uploading(val current: Long, val total: Long) : State()
+ object Success : State()
+ data class Failure(val throwable: Throwable) : State()
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUrlResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUrlResolver.kt
new file mode 100644
index 0000000000..0a20bef809
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUrlResolver.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.content
+
+/**
+ * This interface defines methods for accessing content from the current session.
+ */
+interface ContentUrlResolver {
+
+ enum class ThumbnailMethod(val value: String) {
+ CROP("crop"),
+ SCALE("scale")
+ }
+
+ /**
+ * Get the actual URL for accessing the full-size image of a Matrix media content URI.
+ *
+ * @param contentUrl the Matrix media content URI (in the form of "mxc://...").
+ * @return the URL to access the described resource, or null if the url is invalid.
+ */
+ fun resolveFullSize(contentUrl: String?): String?
+
+ /**
+ * Get the actual URL for accessing the thumbnail image of a given Matrix media content URI.
+ *
+ * @param contentUrl the Matrix media content URI (in the form of "mxc://...").
+ * @param width the desired width
+ * @param height the desired height
+ * @param method the desired method (METHOD_CROP or METHOD_SCALE)
+ * @return the URL to access the described resource, or null if the url is invalid.
+ */
+ fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ThumbnailMethod): String?
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
new file mode 100644
index 0000000000..8e933426d5
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto
+
+import android.content.Context
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.listeners.ProgressListener
+import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
+import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener
+import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
+import im.vector.matrix.android.api.session.events.model.Content
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
+import im.vector.matrix.android.internal.crypto.NewSessionListener
+import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
+import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
+import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
+import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
+import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
+import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
+import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
+import java.io.File
+
+interface CryptoService {
+
+ fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback)
+
+ fun deleteDevice(deviceId: String, callback: MatrixCallback)
+
+ fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback)
+
+ fun getCryptoVersion(context: Context, longFormat: Boolean): String
+
+ fun isCryptoEnabled(): Boolean
+
+ fun getSasVerificationService(): SasVerificationService
+
+ fun getKeysBackupService(): KeysBackupService
+
+ fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean
+
+ fun setWarnOnUnknownDevices(warn: Boolean)
+
+ fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String)
+
+ fun getUserDevices(userId: String): MutableList
+
+ fun setDevicesKnown(devices: List, callback: MatrixCallback?)
+
+ fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo?
+
+ fun getMyDevice(): MXDeviceInfo
+
+ fun getGlobalBlacklistUnverifiedDevices(): Boolean
+
+ fun setGlobalBlacklistUnverifiedDevices(block: Boolean)
+
+ fun setRoomUnBlacklistUnverifiedDevices(roomId: String)
+
+ fun getDeviceTrackingStatus(userId: String): Int
+
+ fun importRoomKeys(roomKeysAsArray: ByteArray, password: String, progressListener: ProgressListener?, callback: MatrixCallback)
+
+ fun exportRoomKeys(password: String, callback: MatrixCallback)
+
+ fun setRoomBlacklistUnverifiedDevices(roomId: String)
+
+ fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo?
+
+ fun reRequestRoomKeyForEvent(event: Event)
+
+ fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody)
+
+ fun addRoomKeysRequestListener(listener: RoomKeysRequestListener)
+
+ fun removeRoomKeysRequestListener(listener: RoomKeysRequestListener)
+
+ fun getDevicesList(callback: MatrixCallback)
+
+ fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int
+
+ fun isRoomEncrypted(roomId: String): Boolean
+
+ fun encryptEventContent(eventContent: Content,
+ eventType: String,
+ roomId: String,
+ callback: MatrixCallback)
+
+ @Throws(MXCryptoError::class)
+ fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult
+
+ fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback)
+
+ fun getEncryptionAlgorithm(roomId: String): String?
+
+ fun shouldEncryptForInvitedMembers(roomId: String): Boolean
+
+ fun downloadKeys(userIds: List, forceDownload: Boolean, callback: MatrixCallback>)
+
+ fun clearCryptoCache(callback: MatrixCallback)
+
+ fun addNewSessionListener(newSessionListener: NewSessionListener)
+
+ fun removeSessionListener(listener: NewSessionListener)
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt
new file mode 100644
index 0000000000..b61a1e4149
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016 OpenMarket Ltd
+ * Copyright 2017 Vector Creations Ltd
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto
+
+import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
+import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
+import org.matrix.olm.OlmException
+
+/**
+ * Represents a crypto error response.
+ */
+sealed class MXCryptoError : Throwable() {
+
+ data class Base(val errorType: ErrorType,
+ val technicalMessage: String,
+ /**
+ * Describe the error with more details
+ */
+ val detailedErrorDescription: String? = null) : MXCryptoError()
+
+ data class OlmError(val olmException: OlmException) : MXCryptoError()
+
+ data class UnknownDevice(val deviceList: MXUsersDevicesMap) : MXCryptoError()
+
+ enum class ErrorType {
+ ENCRYPTING_NOT_ENABLED,
+ UNABLE_TO_ENCRYPT,
+ UNABLE_TO_DECRYPT,
+ UNKNOWN_INBOUND_SESSION_ID,
+ INBOUND_SESSION_MISMATCH_ROOM_ID,
+ MISSING_FIELDS,
+ BAD_EVENT_FORMAT,
+ MISSING_SENDER_KEY,
+ MISSING_CIPHER_TEXT,
+ BAD_DECRYPTED_FORMAT,
+ NOT_INCLUDE_IN_RECIPIENTS,
+ BAD_RECIPIENT,
+ BAD_RECIPIENT_KEY,
+ FORWARDED_MESSAGE,
+ BAD_ROOM,
+ BAD_ENCRYPTED_MESSAGE,
+ DUPLICATED_MESSAGE_INDEX,
+ MISSING_PROPERTY,
+ OLM,
+ UNKNOWN_DEVICES,
+ UNKNOWN_MESSAGE_INDEX
+ }
+
+ companion object {
+ /**
+ * Resource for technicalMessage
+ */
+ const val UNABLE_TO_ENCRYPT_REASON = "Unable to encrypt %s"
+ const val UNABLE_TO_DECRYPT_REASON = "Unable to decrypt %1\$s. Algorithm: %2\$s"
+ const val OLM_REASON = "OLM error: %1\$s"
+ const val DETAILED_OLM_REASON = "Unable to decrypt %1\$s. OLM error: %2\$s"
+ const val UNKNOWN_INBOUND_SESSION_ID_REASON = "Unknown inbound session id"
+ const val INBOUND_SESSION_MISMATCH_ROOM_ID_REASON = "Mismatched room_id for inbound group session (expected %1\$s, was %2\$s)"
+ const val MISSING_FIELDS_REASON = "Missing fields in input"
+ const val BAD_EVENT_FORMAT_TEXT_REASON = "Bad event format"
+ const val MISSING_SENDER_KEY_TEXT_REASON = "Missing senderKey"
+ const val MISSING_CIPHER_TEXT_REASON = "Missing ciphertext"
+ const val BAD_DECRYPTED_FORMAT_TEXT_REASON = "Bad decrypted event format"
+ const val NOT_INCLUDED_IN_RECIPIENT_REASON = "Not included in recipients"
+ const val BAD_RECIPIENT_REASON = "Message was intended for %1\$s"
+ const val BAD_RECIPIENT_KEY_REASON = "Message not intended for this device"
+ const val FORWARDED_MESSAGE_REASON = "Message forwarded from %1\$s"
+ const val BAD_ROOM_REASON = "Message intended for room %1\$s"
+ const val BAD_ENCRYPTED_MESSAGE_REASON = "Bad Encrypted Message"
+ const val DUPLICATE_MESSAGE_INDEX_REASON = "Duplicate message index, possible replay attack %1\$s"
+ const val ERROR_MISSING_PROPERTY_REASON = "No '%1\$s' property. Cannot prevent unknown-key attack"
+ const val UNKNOWN_DEVICES_REASON = "This room contains unknown devices which have not been verified.\n" +
+ "We strongly recommend you verify them before continuing."
+ const val NO_MORE_ALGORITHM_REASON = "Room was previously configured to use encryption, but is no longer." +
+ " Perhaps the homeserver is hiding the configuration event."
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt
new file mode 100644
index 0000000000..6728daa527
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.keysbackup
+
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.listeners.ProgressListener
+import im.vector.matrix.android.api.listeners.StepProgressListener
+import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
+import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
+import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
+import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
+import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
+
+interface KeysBackupService {
+ /**
+ * Retrieve the current version of the backup from the home server
+ *
+ * It can be different than keysBackupVersion.
+ * @param callback onSuccess(null) will be called if there is no backup on the server
+ */
+ fun getCurrentVersion(callback: MatrixCallback)
+
+ /**
+ * Create a new keys backup version and enable it, using the information return from [prepareKeysBackupVersion].
+ *
+ * @param keysBackupCreationInfo the info object from [prepareKeysBackupVersion].
+ * @param callback Asynchronous callback
+ */
+ fun createKeysBackupVersion(keysBackupCreationInfo: MegolmBackupCreationInfo,
+ callback: MatrixCallback)
+
+ /**
+ * Facility method to get the total number of locally stored keys
+ */
+ fun getTotalNumbersOfKeys(): Int
+
+ /**
+ * Facility method to get the number of backed up keys
+ */
+ fun getTotalNumbersOfBackedUpKeys(): Int
+
+ /**
+ * Start to back up keys immediately.
+ *
+ * @param progressListener the callback to follow the progress
+ * @param callback the main callback
+ */
+ fun backupAllGroupSessions(progressListener: ProgressListener?,
+ callback: MatrixCallback?)
+
+ /**
+ * Check trust on a key backup version.
+ *
+ * @param keysBackupVersion the backup version to check.
+ * @param callback block called when the operations completes.
+ */
+ fun getKeysBackupTrust(keysBackupVersion: KeysVersionResult,
+ callback: MatrixCallback)
+
+ /**
+ * Return the current progress of the backup
+ */
+ fun getBackupProgress(progressListener: ProgressListener)
+
+ /**
+ * Get information about a backup version defined on the homeserver.
+ *
+ * It can be different than keysBackupVersion.
+ * @param version the backup version
+ * @param callback
+ */
+ fun getVersion(version: String,
+ callback: MatrixCallback)
+
+ /**
+ * This method fetches the last backup version on the server, then compare to the currently backup version use.
+ * If versions are not the same, the current backup is deleted (on server or locally), then the backup may be started again, using the last version.
+ *
+ * @param callback true if backup is already using the last version, and false if it is not the case
+ */
+ fun forceUsingLastVersion(callback: MatrixCallback)
+
+ /**
+ * Check the server for an active key backup.
+ *
+ * If one is present and has a valid signature from one of the user's verified
+ * devices, start backing up to it.
+ */
+ fun checkAndStartKeysBackup()
+
+ fun addListener(listener: KeysBackupStateListener)
+
+ fun removeListener(listener: KeysBackupStateListener)
+
+ /**
+ * Set up the data required to create a new backup version.
+ * The backup version will not be created and enabled until [createKeysBackupVersion]
+ * is called.
+ * The returned [MegolmBackupCreationInfo] object has a `recoveryKey` member with
+ * the user-facing recovery key string.
+ *
+ * @param password an optional passphrase string that can be entered by the user
+ * when restoring the backup as an alternative to entering the recovery key.
+ * @param progressListener a progress listener, as generating private key from password may take a while
+ * @param callback Asynchronous callback
+ */
+ fun prepareKeysBackupVersion(password: String?,
+ progressListener: ProgressListener?,
+ callback: MatrixCallback)
+
+ /**
+ * Delete a keys backup version. It will delete all backed up keys on the server, and the backup itself.
+ * If we are backing up to this version. Backup will be stopped.
+ *
+ * @param version the backup version to delete.
+ * @param callback Asynchronous callback
+ */
+ fun deleteBackup(version: String,
+ callback: MatrixCallback?)
+
+ /**
+ * Ask if the backup on the server contains keys that we may do not have locally.
+ * This should be called when entering in the state READY_TO_BACKUP
+ */
+ fun canRestoreKeys(): Boolean
+
+ /**
+ * Set trust on a keys backup version.
+ * It adds (or removes) the signature of the current device to the authentication part of the keys backup version.
+ *
+ * @param keysBackupVersion the backup version to check.
+ * @param trust the trust to set to the keys backup.
+ * @param callback block called when the operations completes.
+ */
+ fun trustKeysBackupVersion(keysBackupVersion: KeysVersionResult,
+ trust: Boolean,
+ callback: MatrixCallback)
+
+ /**
+ * Set trust on a keys backup version.
+ *
+ * @param keysBackupVersion the backup version to check.
+ * @param recoveryKey the recovery key to challenge with the key backup public key.
+ * @param callback block called when the operations completes.
+ */
+ fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult,
+ recoveryKey: String,
+ callback: MatrixCallback)
+
+ /**
+ * Set trust on a keys backup version.
+ *
+ * @param keysBackupVersion the backup version to check.
+ * @param password the pass phrase to challenge with the keyBackupVersion public key.
+ * @param callback block called when the operations completes.
+ */
+ fun trustKeysBackupVersionWithPassphrase(keysBackupVersion: KeysVersionResult,
+ password: String,
+ callback: MatrixCallback)
+
+ /**
+ * Restore a backup with a recovery key from a given backup version stored on the homeserver.
+ *
+ * @param keysVersionResult the backup version to restore from.
+ * @param recoveryKey the recovery key to decrypt the retrieved backup.
+ * @param roomId the id of the room to get backup data from.
+ * @param sessionId the id of the session to restore.
+ * @param stepProgressListener the step progress listener
+ * @param callback Callback. It provides the number of found keys and the number of successfully imported keys.
+ */
+ fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult,
+ recoveryKey: String, roomId: String?,
+ sessionId: String?,
+ stepProgressListener: StepProgressListener?,
+ callback: MatrixCallback)
+
+ /**
+ * Restore a backup with a password from a given backup version stored on the homeserver.
+ *
+ * @param keysBackupVersion the backup version to restore from.
+ * @param password the password to decrypt the retrieved backup.
+ * @param roomId the id of the room to get backup data from.
+ * @param sessionId the id of the session to restore.
+ * @param stepProgressListener the step progress listener
+ * @param callback Callback. It provides the number of found keys and the number of successfully imported keys.
+ */
+ fun restoreKeyBackupWithPassword(keysBackupVersion: KeysVersionResult,
+ password: String,
+ roomId: String?,
+ sessionId: String?,
+ stepProgressListener: StepProgressListener?,
+ callback: MatrixCallback)
+
+ val keysBackupVersion: KeysVersionResult?
+ val currentBackupVersion: String?
+ val isEnabled: Boolean
+ val isStucked: Boolean
+ val state: KeysBackupState
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupState.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupState.kt
new file mode 100644
index 0000000000..a1bd29e78d
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupState.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.keysbackup
+
+/**
+ * E2e keys backup states.
+ *
+ *
+ * |
+ * V deleteKeyBackupVersion (on current backup)
+ * +----------------------> UNKNOWN <-------------
+ * | |
+ * | | checkAndStartKeysBackup (at startup or on new verified device or a new detected backup)
+ * | V
+ * | CHECKING BACKUP
+ * | |
+ * | Network error |
+ * +<----------+----------------+-------> DISABLED <----------------------+
+ * | | | | |
+ * | | | | createKeysBackupVersion |
+ * | V | V |
+ * +<--- WRONG VERSION | ENABLING |
+ * | ^ | | |
+ * | | V ok | error |
+ * | | +------> READY <--------+----------------------------+
+ * V | | |
+ * NOT TRUSTED | | | on new key
+ * | | V
+ * | | WILL BACK UP (waiting a random duration)
+ * | | |
+ * | | |
+ * | | ok V
+ * | +----- BACKING UP
+ * | |
+ * | Error |
+ * +<---------------+
+ *
+ */
+enum class KeysBackupState {
+ // Need to check the current backup version on the homeserver
+ Unknown,
+ // Checking if backup is enabled on home server
+ CheckingBackUpOnHomeserver,
+ // Backup has been stopped because a new backup version has been detected on the homeserver
+ WrongBackUpVersion,
+ // Backup from this device is not enabled
+ Disabled,
+ // 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
+ // Use [KeysBackup.getKeysBackupTrust()] to get trust details.
+ // Consequently, the backup from this device is not enabled.
+ NotTrusted,
+ // Backup is being enabled: the backup version is being created on the homeserver
+ Enabling,
+ // Backup is enabled and ready to send backup to the homeserver
+ ReadyToBackUp,
+ // e2e keys are going to be sent to the homeserver
+ WillBackUp,
+ // e2e keys are being sent to the homeserver
+ BackingUp
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupStateListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupStateListener.kt
new file mode 100644
index 0000000000..f145d779f1
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupStateListener.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.keysbackup
+
+interface KeysBackupStateListener {
+
+ /**
+ * The keys backup state has changed
+ * @param newState the new state
+ */
+ fun onStateChange(newState: KeysBackupState)
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keyshare/RoomKeysRequestListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keyshare/RoomKeysRequestListener.kt
new file mode 100644
index 0000000000..5bce27e1b4
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keyshare/RoomKeysRequestListener.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.keyshare
+
+import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
+import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCancellation
+
+/**
+ * Room keys events listener
+ */
+interface RoomKeysRequestListener {
+ /**
+ * An room key request has been received.
+ *
+ * @param request the request
+ */
+ fun onRoomKeyRequest(request: IncomingRoomKeyRequest)
+
+ /**
+ * A room key request cancellation has been received.
+ *
+ * @param request the cancellation request
+ */
+ fun onRoomKeyRequestCancellation(request: IncomingRoomKeyRequestCancellation)
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/CancelCode.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/CancelCode.kt
new file mode 100644
index 0000000000..d999d06fc5
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/CancelCode.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.session.crypto.sas
+
+enum class CancelCode(val value: String, val humanReadable: String) {
+ User("m.user", "the user cancelled the verification"),
+ Timeout("m.timeout", "the verification process timed out"),
+ UnknownTransaction("m.unknown_transaction", "the device does not know about that transaction"),
+ UnknownMethod("m.unknown_method", "the device can’t agree on a key agreement, hash, MAC, or SAS method"),
+ MismatchedCommitment("m.mismatched_commitment", "the hash commitment did not match"),
+ MismatchedSas("m.mismatched_sas", "the SAS did not match"),
+ UnexpectedMessage("m.unexpected_message", "the device received an unexpected message"),
+ InvalidMessage("m.invalid_message", "an invalid message was received"),
+ MismatchedKeys("m.key_mismatch", "Key mismatch"),
+ UserMismatchError("m.user_error", "User mismatch")
+}
+
+fun safeValueOf(code: String?): CancelCode {
+ return CancelCode.values().firstOrNull { code == it.value } ?: CancelCode.User
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/EmojiRepresentation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/EmojiRepresentation.kt
new file mode 100644
index 0000000000..0bcb96a999
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/EmojiRepresentation.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.sas
+
+import androidx.annotation.StringRes
+
+data class EmojiRepresentation(val emoji: String,
+ @StringRes val nameResId: Int)
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/IncomingSasVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/IncomingSasVerificationTransaction.kt
new file mode 100644
index 0000000000..791c63dc15
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/IncomingSasVerificationTransaction.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.sas
+
+interface IncomingSasVerificationTransaction {
+ val uxState: UxState
+
+ fun performAccept()
+
+ enum class UxState {
+ UNKNOWN,
+ SHOW_ACCEPT,
+ WAIT_FOR_KEY_AGREEMENT,
+ SHOW_SAS,
+ WAIT_FOR_VERIFICATION,
+ VERIFIED,
+ CANCELLED_BY_ME,
+ CANCELLED_BY_OTHER
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/Mode.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/Mode.kt
new file mode 100644
index 0000000000..da72e98bad
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/Mode.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.sas
+
+object SasMode {
+ const val DECIMAL = "decimal"
+ const val EMOJI = "emoji"
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/OutgoingSasVerificationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/OutgoingSasVerificationRequest.kt
new file mode 100644
index 0000000000..6aeed55549
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/OutgoingSasVerificationRequest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.sas
+
+interface OutgoingSasVerificationRequest {
+ val uxState: UxState
+
+ enum class UxState {
+ UNKNOWN,
+ WAIT_FOR_START,
+ WAIT_FOR_KEY_AGREEMENT,
+ SHOW_SAS,
+ WAIT_FOR_VERIFICATION,
+ VERIFIED,
+ CANCELLED_BY_ME,
+ CANCELLED_BY_OTHER
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt
new file mode 100644
index 0000000000..a11c69cd7e
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.sas
+
+interface SasVerificationService {
+ fun addListener(listener: SasVerificationListener)
+
+ fun removeListener(listener: SasVerificationListener)
+
+ fun markedLocallyAsManuallyVerified(userId: String, deviceID: String)
+
+ fun getExistingTransaction(otherUser: String, tid: String): SasVerificationTransaction?
+
+ fun beginKeyVerificationSAS(userId: String, deviceID: String): String?
+
+ fun beginKeyVerification(method: String, userId: String, deviceID: String): String?
+
+ // fun transactionUpdated(tx: SasVerificationTransaction)
+
+ interface SasVerificationListener {
+ fun transactionCreated(tx: SasVerificationTransaction)
+ fun transactionUpdated(tx: SasVerificationTransaction)
+ fun markedAsManuallyVerified(userId: String, deviceId: String)
+ }
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt
new file mode 100644
index 0000000000..dc489cf644
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.sas
+
+interface SasVerificationTransaction {
+ val state: SasVerificationTxState
+
+ val cancelledReason: CancelCode?
+
+ val transactionId: String
+
+ val otherUserId: String
+
+ var otherDeviceId: String?
+
+ val isIncoming: Boolean
+
+ fun supportsEmoji(): Boolean
+
+ fun supportsDecimal(): Boolean
+
+ fun getEmojiCodeRepresentation(): List
+
+ fun getDecimalCodeRepresentation(): String
+
+ /**
+ * User wants to cancel the transaction
+ */
+ fun cancel()
+
+ /**
+ * To be called by the client when the user has verified that
+ * both short codes do match
+ */
+ fun userHasVerifiedShortCode()
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTxState.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTxState.kt
new file mode 100644
index 0000000000..60ce8f0cc6
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTxState.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.api.session.crypto.sas
+
+enum class SasVerificationTxState {
+ None,
+ // I have started a verification request
+ SendingStart,
+ Started,
+ // Other user/device sent me a request
+ OnStarted,
+ // I have accepted a request started by the other user/device
+ SendingAccept,
+ Accepted,
+ // My request has been accepted by the other user/device
+ OnAccepted,
+ // I have sent my public key
+ SendingKey,
+ KeySent,
+ // The other user/device has sent me his public key
+ OnKeyReceived,
+ // Short code is ready to be displayed
+ ShortCodeReady,
+ // I have compared the code and manually said that they match
+ ShortCodeAccepted,
+
+ SendingMac,
+ MacSent,
+ Verifying,
+ Verified,
+
+ //Global: The verification has been cancelled (by me or other), see cancelReason for details
+ Cancelled,
+ OnCancelled
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/AggregatedAnnotation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/AggregatedAnnotation.kt
new file mode 100644
index 0000000000..3f1c619906
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/AggregatedAnnotation.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.session.events.model
+
+import com.squareup.moshi.JsonClass
+
+/**
+ *
+ * {
+ * "chunk": [
+ * {
+ * "type": "m.reaction",
+ * "key": "👍",
+ * "count": 3
+ * }
+ * ],
+ * "limited": false,
+ * "count": 1
+ * },
+ *
+ */
+
+@JsonClass(generateAdapter = true)
+data class AggregatedAnnotation (
+ override val limited: Boolean? = false,
+ override val count: Int? = 0,
+ val chunk: List? = null
+
+) : UnsignedRelationInfo
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/AggregatedRelations.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/AggregatedRelations.kt
new file mode 100644
index 0000000000..0f8d21f532
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/AggregatedRelations.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.session.events.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+/**
+ *
+ * {
+ * "m.annotation": {
+ * "chunk": [
+ * {
+ * "type": "m.reaction",
+ * "key": "👍",
+ * "count": 3
+ * }
+ * ],
+ * "limited": false,
+ * "count": 1
+ * },
+ * "m.reference": {
+ * "chunk": [
+ * {
+ * "type": "m.room.message",
+ * "event_id": "$some_event_id"
+ * }
+ * ],
+ * "limited": false,
+ * "count": 1
+ * }
+ * }
+ *
+ */
+
+@JsonClass(generateAdapter = true)
+data class AggregatedRelations(
+ @Json(name = "m.annotation") val annotations: AggregatedAnnotation? = null,
+ @Json(name = "m.reference") val references: DefaultUnsignedRelationInfo? = null
+)
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/DefaultUnsignedRelationInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/DefaultUnsignedRelationInfo.kt
new file mode 100644
index 0000000000..3e2df0aaf2
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/DefaultUnsignedRelationInfo.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.api.session.events.model
+
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class DefaultUnsignedRelationInfo(
+ override val limited: Boolean? = false,
+ override val count: Int? = 0,
+ val chunk: List