diff --git a/CHANGES.md b/CHANGES.md index fde0a6d9d33dca374b2ed4ca84c544d3e9826eec..3e03353813bbadf5ae408268157561ef8beb1206 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,15 @@ Please also refer to the Changelog of Element Android: https://github.com/vector-im/element-android/blob/main/CHANGES.md +Changes in Matrix-SDK 1.3.0 (2021-09-27) +=================================================== + +Imported from Element 1.3.0. (https://github.com/vector-im/element-android/releases/tag/v1.3.0) + +SDK API changes âš ï¸ +------------------ + - InitialSyncProgressService has been renamed to SyncStatusService and its function getInitialSyncProgressStatus() has been renamed to getSyncStatusLive() ([#4046](https://github.com/vector-im/element-android/issues/4046)) + + Changes in Matrix-SDK 1.2.2 (2021-09-13) =================================================== diff --git a/build.gradle b/build.gradle index 01c59b73575f3575b1b4f670405b2ba8a73078e9..f4ef35a2608ff753634ca2d5ff0f53a89bd4aa0b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,8 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - // Ref: https://kotlinlang.org/releases.html - ext.kotlin_version = '1.5.21' - ext.kotlin_coroutines_version = "1.5.0" + apply from: 'dependencies.gradle' + repositories { google() jcenter() @@ -13,9 +12,9 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.vanniktech:gradle-maven-publish-plugin:0.17.0' + classpath libs.gradle.gradlePlugin + classpath libs.gradle.kotlinPlugin + classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/dependencies.gradle b/dependencies.gradle new file mode 100644 index 0000000000000000000000000000000000000000..a4e2c6038781a65f5265f6d5b3c9404489327e33 --- /dev/null +++ b/dependencies.gradle @@ -0,0 +1,130 @@ +ext.versions = [ + + 'minSdk' : 21, + 'compileSdk' : 30, + 'targetSdk' : 30, + 'sourceCompat' : JavaVersion.VERSION_11, + 'targetCompat' : JavaVersion.VERSION_11, +] + +def gradle = "7.0.2" +// Ref: https://kotlinlang.org/releases.html +def kotlin = "1.5.30" +def kotlinCoroutines = "1.5.1" +def dagger = "2.38.1" +def retrofit = "2.9.0" +def arrow = "0.8.2" +def markwon = "4.6.2" +def moshi = "1.12.0" +def lifecycle = "2.2.0" +def rxBinding = "3.1.0" +def epoxy = "4.6.2" +def glide = "4.12.0" +def bigImageViewer = "1.8.1" +def jjwt = "0.11.2" + +// Testing +def mockk = "1.12.0" +def espresso = "3.4.0" +def androidxTest = "1.4.0" + + +ext.libs = [ + gradle : [ + 'gradlePlugin' : "com.android.tools.build:gradle:$gradle", + 'kotlinPlugin' : "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin" + ], + jetbrains : [ + 'kotlinStdlibJdk7' : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin", + 'kotlinStdlib' : "org.jetbrains.kotlin:kotlin-stdlib:$kotlin", + 'coroutinesCore' : "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutines", + 'coroutinesAndroid' : "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutines", + 'coroutinesRx2' : "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlinCoroutines" + ], + androidx : [ + 'appCompat' : "androidx.appcompat:appcompat:1.3.1", + 'core' : "androidx.core:core-ktx:1.6.0", + 'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1", + 'exifinterface' : "androidx.exifinterface:exifinterface:1.3.3", + 'fragmentKtx' : "androidx.fragment:fragment-ktx:1.3.6", + 'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.0", + 'work' : "androidx.work:work-runtime-ktx:2.5.0", + 'autoFill' : "androidx.autofill:autofill:1.1.0", + 'preferenceKtx' : "androidx.preference:preference-ktx:1.1.1", + 'junit' : "androidx.test.ext:junit:1.1.3", + 'lifecycleExtensions' : "androidx.lifecycle:lifecycle-extensions:$lifecycle", + 'lifecycleJava8' : "androidx.lifecycle:lifecycle-common-java8:$lifecycle", + 'lifecycleLivedata' : "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1", + 'datastore' : "androidx.datastore:datastore:1.0.0", + 'datastorepreferences' : "androidx.datastore:datastore-preferences:1.0.0", + 'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2", + 'coreTesting' : "androidx.arch.core:core-testing:2.1.0", + 'testCore' : "androidx.test:core:$androidxTest", + 'orchestrator' : "androidx.test:orchestrator:$androidxTest", + 'testRunner' : "androidx.test:runner:$androidxTest", + 'testRules' : "androidx.test:rules:$androidxTest", + 'espressoCore' : "androidx.test.espresso:espresso-core:$espresso", + 'espressoContrib' : "androidx.test.espresso:espresso-contrib:$espresso", + 'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso" + ], + google : [ + 'material' : "com.google.android.material:material:1.4.0" + ], + dagger : [ + 'dagger' : "com.google.dagger:dagger:$dagger", + 'daggerCompiler' : "com.google.dagger:dagger-compiler:$dagger" + ], + squareup : [ + 'moshi' : "com.squareup.moshi:moshi-adapters:$moshi", + 'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi", + 'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit", + 'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit" + ], + rx : [ + 'rxKotlin' : "io.reactivex.rxjava2:rxkotlin:2.4.0", + 'rxAndroid' : "io.reactivex.rxjava2:rxandroid:2.1.1" + ], + arrow : [ + 'core' : "io.arrow-kt:arrow-core:$arrow", + 'instances' : "io.arrow-kt:arrow-instances-core:$arrow" + ], + markwon : [ + 'core' : "io.noties.markwon:core:$markwon", + 'html' : "io.noties.markwon:html:$markwon" + ], + airbnb : [ + 'epoxy' : "com.airbnb.android:epoxy:$epoxy", + 'epoxyGlide' : "com.airbnb.android:epoxy-glide-preloading:$epoxy", + 'epoxyProcessor' : "com.airbnb.android:epoxy-processor:$epoxy", + 'epoxyPaging' : "com.airbnb.android:epoxy-paging:$epoxy", + 'mvrx' : "com.airbnb.android:mvrx:1.5.1" + ], + mockk : [ + 'mockk' : "io.mockk:mockk:$mockk", + 'mockkAndroid' : "io.mockk:mockk-android:$mockk" + ], + github : [ + 'glide' : "com.github.bumptech.glide:glide:$glide", + 'glideCompiler' : "com.github.bumptech.glide:compiler:$glide", + 'bigImageViewer' : "com.github.piasy:BigImageViewer:$bigImageViewer", + 'glideImageLoader' : "com.github.piasy:GlideImageLoader:$bigImageViewer", + 'progressPieIndicator' : "com.github.piasy:ProgressPieIndicator:$bigImageViewer", + 'glideImageViewFactory' : "com.github.piasy:GlideImageViewFactory:$bigImageViewer" + ], + jakewharton : [ + 'timber' : "com.jakewharton.timber:timber:5.0.1", + 'rxbinding' : "com.jakewharton.rxbinding3:rxbinding:$rxBinding", + 'rxbindingAppcompat' : "com.jakewharton.rxbinding3:rxbinding-appcompat:$rxBinding", + 'rxbindingMaterial' : "com.jakewharton.rxbinding3:rxbinding-material:$rxBinding" + ], + jsonwebtoken: [ + 'jjwtApi' : "io.jsonwebtoken:jjwt-api:$jjwt", + 'jjwtImpl' : "io.jsonwebtoken:jjwt-impl:$jjwt", + 'jjwtOrgjson' : "io.jsonwebtoken:jjwt-orgjson:$jjwt" + ], + tests : [ + 'kluent' : "org.amshove.kluent:kluent-android:1.68", + 'timberJunitRule' : "net.lachlanmckee:timber-junit-rule:1.0.1", + 'junit' : "junit:junit:4.13.2" + ] +] \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 16034b7377454ffdf66088b24000fcf2d664a849..91fe960c5b4525a5c42d85c0fbd48ac213061dca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,7 +26,7 @@ vector.httpLogLevel=NONE # Ref: https://github.com/vanniktech/gradle-maven-publish-plugin GROUP=org.matrix.android POM_ARTIFACT_ID=matrix-android-sdk2 -VERSION_NAME=1.2.2 +VERSION_NAME=1.3.0 POM_PACKAGING=aar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 17ba19021b848c06cb45184d10d07ac47964d70d..a37233c5e2ea795e2bfbe0e7aed8eb2dcb045024 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=9bb8bc05f562f2d42bdf1ba8db62f6b6fa1c3bf6c392228802cc7cb0578fe7e0 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip +distributionSha256Sum=a8da5b02437a60819cad23e10fc7e9cf32bcb57029d9cb277e26eeff76ce014b +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index e1e1383d02d028d5a5a08989a0a503121d9d17a8..68447804a9737e99ae3bd33a03219380e93198aa 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-parcelize' apply plugin: 'realm-android' +// WARNING: always restore this line after importing code from Element Android (1/2) apply plugin: "com.vanniktech.maven.publish" buildscript { @@ -15,14 +16,14 @@ buildscript { } android { - compileSdkVersion 30 testOptions.unitTests.includeAndroidResources = true + compileSdk versions.compileSdk + defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.2.2" + minSdk versions.minSdk + targetSdk versions.targetSdk + // Multidex is useful for tests multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -32,9 +33,8 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - // Seems that the build tools 4.1.0 does not generate BuildConfig.VERSION_NAME anymore. - // Add it manually here. We may remove this trick in the future - buildConfigField "String", "VERSION_NAME", "\"0.0.1\"" + // WARNING: always restore this line after importing code from Element Android (2/2) + buildConfigField "String", "SDK_VERSION", "\"${project.getProperties().getOrDefault("VERSION_NAME", "0.0.0")}\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" @@ -69,8 +69,8 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility versions.sourceCompat + targetCompatibility versions.targetCompat } kotlinOptions { @@ -87,7 +87,7 @@ android { } } -static def gitRevision() { + static def gitRevision() { def cmd = "git rev-parse --short=8 HEAD" return cmd.execute().text.trim() } @@ -104,92 +104,83 @@ static def gitRevisionDate() { dependencies { - def arrow_version = "0.8.2" - def moshi_version = '1.12.0' - def lifecycle_version = '2.2.0' - def arch_version = '2.1.0' - def markwon_version = '3.1.0' - def daggerVersion = '2.38.1' - def work_version = '2.5.0' - def retrofit_version = '2.9.0' - - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" + implementation libs.jetbrains.kotlinStdlibJdk7 + implementation libs.jetbrains.coroutinesCore + implementation libs.jetbrains.coroutinesAndroid - implementation "androidx.appcompat:appcompat:1.3.1" - implementation "androidx.core:core-ktx:1.6.0" + implementation libs.androidx.appCompat + implementation libs.androidx.core - implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" - implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + implementation libs.androidx.lifecycleExtensions + implementation libs.androidx.lifecycleJava8 // Network - implementation "com.squareup.retrofit2:retrofit:$retrofit_version" - implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" + implementation libs.squareup.retrofit + implementation libs.squareup.retrofitMoshi implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.1")) implementation 'com.squareup.okhttp3:okhttp' implementation 'com.squareup.okhttp3:logging-interceptor' implementation 'com.squareup.okhttp3:okhttp-urlconnection' - implementation "com.squareup.moshi:moshi-adapters:$moshi_version" - kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" + implementation libs.squareup.moshi + kapt libs.squareup.moshiKotlin - implementation "ru.noties.markwon:core:$markwon_version" + implementation libs.markwon.core // Image - implementation 'androidx.exifinterface:exifinterface:1.3.3' + implementation libs.androidx.exifinterface // Database implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' kapt 'dk.ilios:realmfieldnameshelper:2.0.0' // Work - implementation "androidx.work:work-runtime-ktx:$work_version" + implementation libs.androidx.work // FP - implementation "io.arrow-kt:arrow-core:$arrow_version" - implementation "io.arrow-kt:arrow-instances-core:$arrow_version" + implementation libs.arrow.core + implementation libs.arrow.instances // olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm implementation 'org.matrix.gitlab.matrix-org:olm:3.2.4' // DI - implementation "com.google.dagger:dagger:$daggerVersion" - kapt "com.google.dagger:dagger-compiler:$daggerVersion" + implementation libs.dagger.dagger + kapt libs.dagger.daggerCompiler // Logging - implementation 'com.jakewharton.timber:timber:5.0.1' + implementation libs.jakewharton.timber implementation 'com.facebook.stetho:stetho-okhttp3:1.6.0' // Video compression - implementation 'com.otaliastudios:transcoder:0.10.3' + implementation 'com.otaliastudios:transcoder:0.10.4' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.31' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.33' - testImplementation 'junit:junit:4.13.2' - testImplementation 'org.robolectric:robolectric:4.5.1' + testImplementation libs.tests.junit + testImplementation 'org.robolectric:robolectric:4.6.1' //testImplementation 'org.robolectric:shadows-support-v4:3.0' // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 - testImplementation 'io.mockk:mockk:1.12.0' - testImplementation 'org.amshove.kluent:kluent-android:1.68' - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" + testImplementation libs.mockk.mockk + testImplementation libs.tests.kluent + implementation libs.jetbrains.coroutinesAndroid // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' - kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion" - androidTestImplementation 'androidx.test:core:1.4.0' - androidTestImplementation 'androidx.test:runner:1.4.0' - androidTestImplementation 'androidx.test:rules:1.4.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation 'org.amshove.kluent:kluent-android:1.68' - androidTestImplementation 'io.mockk:mockk-android:1.12.0' - androidTestImplementation "androidx.arch.core:core-testing:$arch_version" - androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" + kaptAndroidTest libs.dagger.daggerCompiler + androidTestImplementation libs.androidx.testCore + androidTestImplementation libs.androidx.testRunner + androidTestImplementation libs.androidx.testRules + androidTestImplementation libs.androidx.junit + androidTestImplementation libs.androidx.espressoCore + androidTestImplementation libs.tests.kluent + androidTestImplementation libs.mockk.mockkAndroid + androidTestImplementation libs.androidx.coreTesting + androidTestImplementation libs.jetbrains.coroutinesAndroid // Plant Timber tree for test - androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' + androidTestImplementation libs.tests.timberJunitRule - androidTestUtil 'androidx.test:orchestrator:1.4.0' + androidTestUtil libs.androidx.orchestrator } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt index c439da8407bcde21a93c78b2d78ff4272c2a234c..8b9b6efa119b34edaf1d5ab7cb4972243276ae7f 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt @@ -117,7 +117,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo } fun getSdkVersion(): String { - return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")" + return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")" } } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index 7817351e534275101965fcfef52d90293966d2cd..cf9b8f87c1a1f0b76feb40c51592bf1640515471 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -19,6 +19,8 @@ package org.matrix.android.sdk.common import android.content.Context import android.net.Uri import androidx.lifecycle.Observer +import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay @@ -59,13 +61,15 @@ class CommonTestHelper(context: Context) { fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor init { - Matrix.initialize( - context, - MatrixConfiguration( - applicationFlavor = "TestFlavor", - roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider() - ) - ) + UiThreadStatement.runOnUiThread { + Matrix.initialize( + context, + MatrixConfiguration( + applicationFlavor = "TestFlavor", + roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider() + ) + ) + } matrix = Matrix.getInstance(context) } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt index 301cdea461029c84b48e58bba745e31b906b69ef..436daf001be4f178fc69da3a04d46ce4566e70fa 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt @@ -32,10 +32,19 @@ import org.junit.runners.JUnit4 import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.query.ActiveSpaceFilter +import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams +import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset +import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.SessionTestParams @@ -386,6 +395,8 @@ class SpaceHierarchyTest : InstrumentedTest { // The room should have disapear from flat children GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) } } + + commonTestHelper.signOutAndClose(session) } data class TestSpaceCreationResult( @@ -434,6 +445,57 @@ class SpaceHierarchyTest : InstrumentedTest { return TestSpaceCreationResult(spaceId, roomIds) } + @Suppress("EXPERIMENTAL_API_USAGE") + private fun createPrivateSpace(session: Session, + spaceName: String, + childInfo: List<Triple<String, Boolean, Boolean?>> + /** Name, auto-join, canonical*/ + ): TestSpaceCreationResult { + var spaceId = "" + commonTestHelper.waitWithLatch { + GlobalScope.launch { + spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false) + it.countDown() + } + } + + val syncedSpace = session.spaceService().getSpace(spaceId) + val viaServers = listOf(session.sessionParams.homeServerHost ?: "") + + val roomIds = + childInfo.map { entry -> + var roomId = "" + commonTestHelper.waitWithLatch { + GlobalScope.launch { + val homeServerCapabilities = session + .getHomeServerCapabilities() + roomId = session.createRoom(CreateRoomParams().apply { + name = entry.first + this.featurePreset = RestrictedRoomPreset( + homeServerCapabilities, + listOf( + RoomJoinRulesAllowEntry.restrictedToRoom(spaceId) + ) + ) + }) + it.countDown() + } + } + roomId + } + + roomIds.forEachIndexed { index, roomId -> + runBlocking { + syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second) + val canonical = childInfo[index].third + if (canonical != null) { + session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers) + } + } + } + return TestSpaceCreationResult(spaceId, roomIds) + } + @Test fun testRootSpaces() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) @@ -473,5 +535,111 @@ class SpaceHierarchyTest : InstrumentedTest { val rootSpaces = session.spaceService().getRootSpaceSummaries() assertEquals("Unexpected number of root spaces ${rootSpaces.map { it.name }}", 2, rootSpaces.size) + + commonTestHelper.signOutAndClose(session) + } + + @Test + fun testParentRelation() { + val aliceSession = commonTestHelper.createAccount("Alice", SessionTestParams(true)) + val bobSession = commonTestHelper.createAccount("Bib", SessionTestParams(true)) + + val spaceAInfo = createPrivateSpace(aliceSession, "Private Space A", listOf( + Triple("General", true /*suggested*/, true/*canonical*/), + Triple("Random", true, true) + )) + + commonTestHelper.runBlockingTest { + aliceSession.getRoom(spaceAInfo.spaceId)!!.invite(bobSession.myUserId, null) + } + + commonTestHelper.runBlockingTest { + bobSession.joinRoom(spaceAInfo.spaceId, null, emptyList()) + } + + var bobRoomId = "" + commonTestHelper.waitWithLatch { + GlobalScope.launch { + bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" }) + bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId) + it.countDown() + } + } + + commonTestHelper.runBlockingTest { + aliceSession.joinRoom(bobRoomId) + } + + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { + aliceSession.getRoomSummary(bobRoomId)?.membership?.isActive() == true + } + } + + commonTestHelper.waitWithLatch { + GlobalScope.launch { + bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) + it.countDown() + } + } + + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { + val stateEvent = aliceSession.getRoom(bobRoomId)!!.getStateEvent(EventType.STATE_SPACE_PARENT, QueryStringValue.Equals(spaceAInfo.spaceId)) + stateEvent != null + } + } + + // This should be an invalid space parent relation, because no opposite child and bob is not admin of the space + commonTestHelper.runBlockingTest { + // we can see the state event + // but it is not valid and room is not in hierarchy + assertTrue("Bob Room should not be listed as a child of the space", aliceSession.getRoomSummary(bobRoomId)?.flattenParentIds?.isEmpty() == true) + } + + // Let's now try to make alice admin of the room + + commonTestHelper.waitWithLatch { + GlobalScope.launch { + val room = bobSession.getRoom(bobRoomId)!! + val currentPLContent = room + .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + ?.let { it.content.toModel<PowerLevelsContent>() } + + val newPowerLevelsContent = currentPLContent + ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) + ?.toContent() + + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!) + it.countDown() + } + } + + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { + val powerLevelsHelper = aliceSession.getRoom(bobRoomId)!! + .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + ?.content + ?.toModel<PowerLevelsContent>() + ?.let { PowerLevelsHelper(it) } + powerLevelsHelper!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT) + } + } + + commonTestHelper.waitWithLatch { + GlobalScope.launch { + aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) + it.countDown() + } + } + + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { + bobSession.getRoomSummary(bobRoomId)?.flattenParentIds?.contains(spaceAInfo.spaceId) == true + } + } + + commonTestHelper.signOutAndClose(aliceSession) + commonTestHelper.signOutAndClose(bobSession) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt index 99802592667e956951a946cae612d5bb2e28d662..8a4526a5e123731e6ed4efdbe997af2bf9a66068 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt @@ -111,7 +111,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo } fun getSdkVersion(): String { - return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")" + return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")" } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt index 51f9b50699b6437f7e3e5a2349e024aa38db03b8..0d204edceed4b4b1527a374227afb87efcea601d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt @@ -24,6 +24,7 @@ package org.matrix.android.sdk.api.logger */ open class LoggerTag(_value: String, parentTag: LoggerTag? = null) { + object SYNC : LoggerTag("SYNC") object VOIP : LoggerTag("VOIP") val value: String = if (parentTag == null) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt index 2f981ffbbedfbe7f58c9f51547816c46b1a2894d..1443a8d3b9cd563118a06fea909a9a4a25fe0986 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt @@ -36,7 +36,7 @@ import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.group.GroupService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.identity.IdentityService -import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.media.MediaService import org.matrix.android.sdk.api.session.openid.OpenIdService @@ -75,7 +75,7 @@ interface Session : ProfileService, PushRuleService, PushersService, - InitialSyncProgressService, + SyncStatusService, HomeServerCapabilitiesService, SecureStorageService, AccountService { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 3d82846e7e98fd9c2370dcfac5969267e7638b88..1f8471c111d0a8f7c828fbbb9341aa50eeb39b8e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -239,7 +239,7 @@ data class Event( fun Event.isTextMessage(): Boolean { return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel<MessageContent>()?.msgType) { + && when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_TEXT, MessageType.MSGTYPE_EMOTE, MessageType.MSGTYPE_NOTICE -> true @@ -249,7 +249,7 @@ fun Event.isTextMessage(): Boolean { fun Event.isImageMessage(): Boolean { return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel<MessageContent>()?.msgType) { + && when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_IMAGE -> true else -> false } @@ -257,7 +257,7 @@ fun Event.isImageMessage(): Boolean { fun Event.isVideoMessage(): Boolean { return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel<MessageContent>()?.msgType) { + && when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_VIDEO -> true else -> false } @@ -265,7 +265,7 @@ fun Event.isVideoMessage(): Boolean { fun Event.isAudioMessage(): Boolean { return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel<MessageContent>()?.msgType) { + && when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_AUDIO -> true else -> false } @@ -273,7 +273,7 @@ fun Event.isAudioMessage(): Boolean { fun Event.isFileMessage(): Boolean { return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel<MessageContent>()?.msgType) { + && when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_FILE -> true else -> false } @@ -281,7 +281,7 @@ fun Event.isFileMessage(): Boolean { fun Event.isAttachmentMessage(): Boolean { return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel<MessageContent>()?.msgType) { + && when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_IMAGE, MessageType.MSGTYPE_AUDIO, MessageType.MSGTYPE_VIDEO, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitialSyncProgressService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt similarity index 55% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitialSyncProgressService.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt index b5d4ef4dbb4a55abe9e713b00ac156111b0d2415..38d47ae1a9b8a0c10bc6f47dfe1f0de74827a249 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitialSyncProgressService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt @@ -17,15 +17,33 @@ package org.matrix.android.sdk.api.session.initsync import androidx.lifecycle.LiveData -interface InitialSyncProgressService { +interface SyncStatusService { - fun getInitialSyncProgressStatus(): LiveData<Status> + fun getSyncStatusLive(): LiveData<Status> sealed class Status { - object Idle : Status() + /** + * For initial sync + */ + abstract class InitialSyncStatus: Status() + + object Idle : InitialSyncStatus() data class Progressing( val initSyncStep: InitSyncStep, val percentProgress: Int = 0 - ) : Status() + ) : InitialSyncStatus() + + /** + * For incremental sync + */ + abstract class IncrementalSyncStatus: Status() + + object IncrementalSyncIdle : IncrementalSyncStatus() + data class IncrementalSyncParsing( + val rooms: Int, + val toDevice: Int + ) : IncrementalSyncStatus() + object IncrementalSyncError : IncrementalSyncStatus() + object IncrementalSyncDone : IncrementalSyncStatus() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt index eed75c9dafb145d16f73981875e5c5601cb61412..b85ab32b2107f1e3fef0b7d4c510de3b2b156391 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt @@ -26,7 +26,14 @@ data class Pusher( val data: PusherData, val state: PusherState -) +) { + companion object { + + const val KIND_EMAIL = "email" + const val KIND_HTTP = "http" + const val APP_ID_EMAIL = "m.email" + } +} enum class PusherState { UNREGISTERED, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt index a5ec100f64cf29bfd6dc19889d2362deb589e98d..2cd17952c688ac2a985960ce00345660bd090120 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt @@ -27,14 +27,12 @@ interface PushersService { /** * Add a new HTTP pusher. - * Note that only `http` kind is supported by the SDK for now. * Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set * * @param pushkey This is a unique identifier for this pusher. The value you should use for * this is the routing or destination address information for the notification, * for example, the APNS token for APNS or the Registration ID for GCM. If your * notification client has no such concept, use any unique identifier. Max length, 512 chars. - * If the kind is "email", this is the email address to send notifications to. * @param appId the application id * This is a reverse-DNS style identifier for the application. It is recommended * that this end with the platform, such that different platform versions get @@ -64,6 +62,30 @@ interface PushersService { append: Boolean, withEventIdOnly: Boolean): UUID + /** + * Add a new Email pusher. + * Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set + * + * @param email The email address to send notifications to. + * @param lang The preferred language for receiving notifications (e.g. "en" or "en-US"). + * @param emailBranding The branding placeholder to include in the email communications. + * @param appDisplayName A human readable string that will allow the user to identify what application owns this pusher. + * @param deviceDisplayName A human readable string that will allow the user to identify what device owns this pusher. + * @param append If true, the homeserver should add another pusher with the given pushkey and App ID in addition + * to any others with different user IDs. Otherwise, the homeserver must remove any other pushers + * with the same App ID and pushkey for different users. Typically We always want to append for + * email pushers since we don't want to stop other accounts notifying to the same email address. + * @return A work request uuid. Can be used to listen to the status + * (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>)) + * @throws [InvalidParameterException] if a parameter is not correct + */ + fun addEmailPusher(email: String, + lang: String, + emailBranding: String, + appDisplayName: String, + deviceDisplayName: String, + append: Boolean = true): UUID + /** * Directly ask the push gateway to send a push to this device * If successful, the push gateway has accepted the request. In this case, the app should receive a Push with the provided eventId. @@ -80,10 +102,23 @@ interface PushersService { eventId: String) /** - * Remove the http pusher + * Remove a registered pusher + * @param pusher the pusher to remove, can be http or email + */ + suspend fun removePusher(pusher: Pusher) + + /** + * Remove a Http pusher by its pushkey and appId + * @see addHttpPusher */ suspend fun removeHttpPusher(pushkey: String, appId: String) + /** + * Remove an Email pusher + * @see addEmailPusher + */ + suspend fun removeEmailPusher(email: String) + /** * Get the current pushers, as a LiveData */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomStrippedState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomStrippedState.kt new file mode 100644 index 0000000000000000000000000000000000000000..dc0c00b282c93efc6958c12fff0f7abecca88d89 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomStrippedState.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.matrix.android.sdk.api.session.room.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * These are the same fields as those returned by /publicRooms, with a few additions: room_type, membership and is_encrypted. + */ +@JsonClass(generateAdapter = true) +data class RoomStrippedState( + /** + * Aliases of the room. May be empty. + */ + @Json(name = "aliases") + val aliases: List<String>? = null, + + /** + * The canonical alias of the room, if any. + */ + @Json(name = "canonical_alias") + val canonicalAlias: String? = null, + + /** + * The name of the room, if any. + */ + @Json(name = "name") + val name: String? = null, + + /** + * Required. The number of members joined to the room. + */ + @Json(name = "num_joined_members") + val numJoinedMembers: Int = 0, + + /** + * Required. The ID of the room. + */ + @Json(name = "room_id") + val roomId: String, + + /** + * The topic of the room, if any. + */ + @Json(name = "topic") + val topic: String? = null, + + /** + * Required. Whether the room may be viewed by guest users without joining. + */ + @Json(name = "world_readable") + val worldReadable: Boolean = false, + + /** + * Required. Whether guest users may join the room and participate in it. If they can, + * they will be subject to ordinary power level rules like any other user. + */ + @Json(name = "guest_can_join") + val guestCanJoin: Boolean = false, + + /** + * The URL for the room's avatar, if one is set. + */ + @Json(name = "avatar_url") + val avatarUrl: String? = null, + + /** + * Undocumented item + */ + @Json(name = "m.federate") + val isFederated: Boolean = false, + + /** + * Optional. If the room is encrypted. This is already accessible as stripped state. + */ + @Json(name = "is_encrypted") + val isEncrypted: Boolean?, + + /** + * Optional. Type of the room, if any, i.e. m.space + */ + @Json(name = "room_type") + val roomType: String?, + + /** + * The current membership of this user in the room. Usually leave if the room is fetched over federation. + */ + @Json(name = "membership") + val membership: String? +) { + /** + * Return the canonical alias, or the first alias from the list of aliases, or null + */ + fun getPrimaryAlias(): String? { + return canonicalAlias ?: aliases?.firstOrNull() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioContent.kt index 1bcb10d88cf753987af8c51e7acf373df5833737..ebf3d127ce48774b1ac12a5796df9b494ff21096 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioContent.kt @@ -28,7 +28,7 @@ data class MessageAudioContent( /** * Required. Must be 'm.audio'. */ - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, /** * Required. A description of the audio e.g. 'Bee Gees - Stayin' Alive', or some kind of content description for accessibility e.g. 'audio attachment'. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageContent.kt index df5641a6220c18197de852e6ac6bd952d842f51e..5a1b66c91c105182a9f7ee9ad1ca800e2f6e3d84 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageContent.kt @@ -20,6 +20,11 @@ import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent interface MessageContent { + + companion object { + const val MSG_TYPE_JSON_KEY = "msgtype" + } + val msgType: String val body: String val relatesTo: RelationDefaultContent? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageDefaultContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageDefaultContent.kt index 65e89cdfee4930b5d4584da44d728bc8f8f55a55..1dadc92271745b86d9bb20293b478d67d07214cb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageDefaultContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageDefaultContent.kt @@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon @JsonClass(generateAdapter = true) data class MessageDefaultContent( - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, @Json(name = "body") override val body: String, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.new_content") override val newContent: Content? = null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEmoteContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEmoteContent.kt index 77983a031eb7e36304e303ebad1d5d07c7d6e1a6..a2ada416ba1bf98e2dd4ec971065a46f5c06e375 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEmoteContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEmoteContent.kt @@ -26,7 +26,7 @@ data class MessageEmoteContent( /** * Required. Must be 'm.emote'. */ - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, /** * Required. The emote action to perform. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageFileContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageFileContent.kt index 96877b4d9f12cce11be1ae623ab45a6806894c99..78f9a5d2f2d2190b47c6e7e5bb51f882131332cf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageFileContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageFileContent.kt @@ -28,7 +28,7 @@ data class MessageFileContent( /** * Required. Must be 'm.file'. */ - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, /** * Required. A human-readable description of the file. This is recommended to be the filename of the original upload. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt index 73fd1eab56522bf0a83278ba66ace436b1b3b5ca..ea7ab5068876785978d24db22534849fd21d35a5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt @@ -27,7 +27,7 @@ data class MessageImageContent( /** * Required. Must be 'm.image'. */ - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, /** * Required. A textual representation of the image. This could be the alt text of the image, the filename of the image, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt index bdb54910dd0183d612b4d4bdb9ed38cd7c471949..6881c0992467b75ea428d48c71226e491ea1b8c9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt @@ -26,7 +26,7 @@ data class MessageLocationContent( /** * Required. Must be 'm.location'. */ - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, /** * Required. A description of the location e.g. 'Big Ben, London, UK', or some kind diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageNoticeContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageNoticeContent.kt index b2fd8cb0c0c09e8cbaf0007acb407ff4b9a7e9a2..dd960355cad6d3a8124132a4fe59df5a30ae4039 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageNoticeContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageNoticeContent.kt @@ -26,7 +26,7 @@ data class MessageNoticeContent( /** * Required. Must be 'm.notice'. */ - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, /** * Required. The notice text to send. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt index 7924469884c6bcfa187389d10a11099b1a3a7c7d..7a1a99bd5fd2b53304e6fff87550a76ff0869fbd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt @@ -30,7 +30,7 @@ const val OPTION_TYPE_BUTTONS = "org.matrix.buttons" */ @JsonClass(generateAdapter = true) data class MessageOptionsContent( - @Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_OPTIONS, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_OPTIONS, @Json(name = "type") val optionType: String? = null, @Json(name = "body") override val body: String, @Json(name = "label") val label: String?, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt index d827475277ec7b38865e41e61149c03ab9164359..9edfe118b09f27b31148ee5f30c4d28968c28629 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt @@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon */ @JsonClass(generateAdapter = true) data class MessagePollResponseContent( - @Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_RESPONSE, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_RESPONSE, @Json(name = "body") override val body: String, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.new_content") override val newContent: Content? = null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageTextContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageTextContent.kt index e45245a9bf1886b0eb8882596c118f8e280e27b8..5968fecd43b82ffb788790f9ab2561dfd3abca6c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageTextContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageTextContent.kt @@ -26,7 +26,7 @@ data class MessageTextContent( /** * Required. Must be 'm.text'. */ - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, /** * Required. The body of the message. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVerificationRequestContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVerificationRequestContent.kt index 25b5f4481500d88d4eec9392db216d025c03772b..b2b3cdac9090b231389aa2799e04e75b81cf5c2b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVerificationRequestContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVerificationRequestContent.kt @@ -24,7 +24,7 @@ import org.matrix.android.sdk.internal.crypto.verification.VerificationInfoReque @JsonClass(generateAdapter = true) data class MessageVerificationRequestContent( - @Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_VERIFICATION_REQUEST, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY)override val msgType: String = MessageType.MSGTYPE_VERIFICATION_REQUEST, @Json(name = "body") override val body: String, @Json(name = "from_device") override val fromDevice: String?, @Json(name = "methods") override val methods: List<String>, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVideoContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVideoContent.kt index 3f5d2dab2e770d189b538b71d16d07fef5c1627c..e1b0cd860741a7ef2036dabd15b6d46d76bc8439 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVideoContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVideoContent.kt @@ -27,7 +27,7 @@ data class MessageVideoContent( /** * Required. Must be 'm.video'. */ - @Json(name = "msgtype") override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY)override val msgType: String, /** * Required. A description of the video e.g. 'Gangnam style', or some kind of content description for accessibility e.g. 'video attachment'. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt index bcc36b579a3437e0424c52d0529d5022a4ee4420..f40572518f0322b3c4ff783cd4511e8ec60aae38 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt @@ -94,5 +94,7 @@ interface SpaceService { */ suspend fun setSpaceParent(childRoomId: String, parentSpaceId: String, canonical: Boolean, viaServers: List<String>) + suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String) + fun getRootSpaceSummaries(): List<RoomSummary> } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt index 06c667ee4adf8f459a9b08dcf259c8402e3628db..3825a5dab2e4d72284d58a50c8dd969a682584a6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt @@ -71,18 +71,24 @@ internal class InboundGroupSessionStore @Inject constructor( } @Synchronized - fun storeInBoundGroupSession(wrapper: OlmInboundGroupSessionWrapper2) { + fun storeInBoundGroupSession(wrapper: OlmInboundGroupSessionWrapper2, sessionId: String, senderKey: String) { Timber.v("## Inbound: getInboundGroupSession mark as dirty ${wrapper.roomId}-${wrapper.senderKey}") // We want to batch this a bit for performances dirtySession.add(wrapper) + if (sessionCache[CacheKey(sessionId, senderKey)] == null) { + // first time seen, put it in memory cache while waiting for batch insert + // If it's already known, no need to update cache it's already there + sessionCache.put(CacheKey(sessionId, senderKey), wrapper) + } + timerTask?.cancel() timerTask = object : TimerTask() { override fun run() { batchSave() } } - timer.schedule(timerTask!!, 2_000) + timer.schedule(timerTask!!, 300) } @Synchronized diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt index b8f1a9abea84cfc0f6c39f9cfb5a051851f249c9..441dfe4a5d38d278b651bcbd68bd8deb8fd6ee61 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt @@ -577,7 +577,8 @@ internal class MXOlmDevice @Inject constructor( session.keysClaimed = keysClaimed session.forwardingCurve25519KeyChain = forwardingCurve25519KeyChain - store.storeInboundGroupSessions(listOf(session)) + inboundGroupSessionStore.storeInBoundGroupSession(session, sessionId, senderKey) +// store.storeInboundGroupSessions(listOf(session)) return true } @@ -703,7 +704,7 @@ internal class MXOlmDevice @Inject constructor( timelineSet.add(messageIndexKey) } - inboundGroupSessionStore.storeInBoundGroupSession(session) + inboundGroupSessionStore.storeInBoundGroupSession(session, sessionId, senderKey) val payload = try { val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE) val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/UserAgentHolder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/UserAgentHolder.kt index 1a884041280c3ca872b191587591dcba593d81d1..57eab6a8dda0eeab7f2e827b0e99ac7caee9fd65 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/UserAgentHolder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/UserAgentHolder.kt @@ -36,7 +36,7 @@ internal class UserAgentHolder @Inject constructor(private val context: Context, /** * Create an user agent with the application version. - * Ex: Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSDK_X 1.0) + * Ex: Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0) * * @param flavorDescription the flavor description */ @@ -74,13 +74,13 @@ internal class UserAgentHolder @Inject constructor(private val context: Context, // if there is no user agent or cannot parse it if (null == systemUserAgent || systemUserAgent.lastIndexOf(")") == -1 || !systemUserAgent.contains("(")) { userAgent = (appName + "/" + appVersion + " ( Flavour " + flavorDescription - + "; MatrixAndroidSDK_X " + BuildConfig.VERSION_NAME + ")") + + "; MatrixAndroidSdk2 " + BuildConfig.SDK_VERSION + ")") } else { // update userAgent = appName + "/" + appVersion + " " + systemUserAgent.substring(systemUserAgent.indexOf("("), systemUserAgent.lastIndexOf(")") - 1) + "; Flavour " + flavorDescription + - "; MatrixAndroidSDK_X " + BuildConfig.VERSION_NAME + ")" + "; MatrixAndroidSdk2 " + BuildConfig.SDK_VERSION + ")" } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index c2bd1e24edea5dd2f9148fb72c3966283b137ae3..22167bc77ae5b5444f7bc67e41ed71887e6dbe40 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -40,7 +40,7 @@ import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.group.GroupService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService -import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.media.MediaService import org.matrix.android.sdk.api.session.openid.OpenIdService @@ -115,7 +115,7 @@ internal class DefaultSession @Inject constructor( private val contentUploadProgressTracker: ContentUploadStateTracker, private val typingUsersTracker: TypingUsersTracker, private val contentDownloadStateTracker: ContentDownloadStateTracker, - private val initialSyncProgressService: Lazy<InitialSyncProgressService>, + private val syncStatusService: Lazy<SyncStatusService>, private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>, private val accountDataService: Lazy<SessionAccountDataService>, private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>, @@ -141,7 +141,7 @@ internal class DefaultSession @Inject constructor( PushersService by pushersService.get(), EventService by eventService.get(), TermsService by termsService.get(), - InitialSyncProgressService by initialSyncProgressService.get(), + SyncStatusService by syncStatusService.get(), SecureStorageService by secureStorageService.get(), HomeServerCapabilitiesService by homeServerCapabilitiesService.get(), ProfileService by profileService.get(), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index 9a936b73c2346b0ac2d63d49a62b906a66bb8350..2003a66c942bb528b955ec4fc3ad1794bb37dd2f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -43,7 +43,7 @@ import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationMan import org.matrix.android.sdk.internal.session.media.MediaModule import org.matrix.android.sdk.internal.session.openid.OpenIdModule import org.matrix.android.sdk.internal.session.profile.ProfileModule -import org.matrix.android.sdk.internal.session.pushers.AddHttpPusherWorker +import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker import org.matrix.android.sdk.internal.session.pushers.PushersModule import org.matrix.android.sdk.internal.session.room.RoomModule import org.matrix.android.sdk.internal.session.room.relation.SendRelationWorker @@ -127,7 +127,7 @@ internal interface SessionComponent { fun inject(worker: SyncWorker) - fun inject(worker: AddHttpPusherWorker) + fun inject(worker: AddPusherWorker) fun inject(worker: SendVerificationMessageWorker) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index cb29cb4819c07ca7a2c779b5156773b5f97bfe80..dc59277f64e97688f27e3d8acd61781825f820ca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -37,7 +37,7 @@ import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService import org.matrix.android.sdk.api.session.events.EventService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService -import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.openid.OpenIdService import org.matrix.android.sdk.api.session.permalinks.PermalinkService import org.matrix.android.sdk.api.session.securestorage.SecureStorageService @@ -81,7 +81,7 @@ import org.matrix.android.sdk.internal.session.download.DownloadProgressIntercep import org.matrix.android.sdk.internal.session.events.DefaultEventService import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService -import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService +import org.matrix.android.sdk.internal.session.initsync.DefaultSyncStatusService import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService @@ -355,7 +355,7 @@ internal abstract class SessionModule { abstract fun bindEventSenderProcessorAsSessionLifecycleObserver(processor: EventSenderProcessorCoroutine): SessionLifecycleObserver @Binds - abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService + abstract fun bindSyncStatusService(service: DefaultSyncStatusService): SyncStatusService @Binds abstract fun bindSecureStorageService(service: DefaultSecureStorageService): SecureStorageService diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt index fdb6caf53fe0109fdef11900fd2f4bdebefdf849..acd163450c0d4aa346b2a6f109919cc09698c704 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt @@ -20,10 +20,16 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import dagger.Lazy +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.SessionLifecycleObserver +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.identity.FoundThreePid @@ -36,23 +42,17 @@ import org.matrix.android.sdk.internal.di.AuthenticatedIdentity import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.extensions.observeNotNull import org.matrix.android.sdk.internal.network.RetrofitFactory -import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.identity.data.IdentityStore +import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult import org.matrix.android.sdk.internal.session.openid.GetOpenIdTokenTask import org.matrix.android.sdk.internal.session.profile.BindThreePidsTask import org.matrix.android.sdk.internal.session.profile.UnbindThreePidsTask import org.matrix.android.sdk.internal.session.sync.model.accountdata.IdentityServerContent -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes -import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask +import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.ensureProtocol -import kotlinx.coroutines.withContext -import okhttp3.OkHttpClient -import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult import timber.log.Timber import javax.inject.Inject import javax.net.ssl.HttpsURLConnection @@ -202,6 +202,8 @@ internal class DefaultIdentityService @Inject constructor( identityStore.setUrl(urlCandidate) identityStore.setToken(token) + // could we remember if it was previously given? + identityStore.setUserConsent(false) updateIdentityAPI(urlCandidate) updateAccountData(urlCandidate) @@ -230,6 +232,8 @@ internal class DefaultIdentityService @Inject constructor( } override suspend fun lookUp(threePids: List<ThreePid>): List<FoundThreePid> { + if (getCurrentIdentityServerUrl() == null) throw IdentityServiceError.NoIdentityServerConfigured + if (!getUserConsent()) { throw IdentityServiceError.UserConsentNotProvided } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt similarity index 78% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt index eb3e3066b128ee15d6e5af97ad01eef954ece171..6dac9bffd0edec413651176f83d27838a07c09e9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt @@ -18,23 +18,28 @@ package org.matrix.android.sdk.internal.session.initsync import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import org.matrix.android.sdk.api.session.initsync.InitSyncStep -import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.internal.session.SessionScope import javax.inject.Inject @SessionScope -internal class DefaultInitialSyncProgressService @Inject constructor() - : InitialSyncProgressService, +internal class DefaultSyncStatusService @Inject constructor() + : SyncStatusService, ProgressReporter { - private val status = MutableLiveData<InitialSyncProgressService.Status>() + private val status = MutableLiveData<SyncStatusService.Status>() private var rootTask: TaskInfo? = null - override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status> { + override fun getSyncStatusLive(): LiveData<SyncStatusService.Status> { return status } + // Only to be used for incremental sync + fun setStatus(newStatus: SyncStatusService.Status.IncrementalSyncStatus) { + status.postValue(newStatus) + } + /** * Create a rootTask */ @@ -67,7 +72,7 @@ internal class DefaultInitialSyncProgressService @Inject constructor() // Update the progress of the leaf and all its parents leaf.setProgress(progress) // Then update the live data using leaf wording and root progress - status.postValue(InitialSyncProgressService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt())) + status.postValue(SyncStatusService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt())) } } } @@ -82,13 +87,13 @@ internal class DefaultInitialSyncProgressService @Inject constructor() // And close it endedTask.parent.child = null } else { - status.postValue(InitialSyncProgressService.Status.Idle) + status.postValue(SyncStatusService.Status.Idle) } } } fun endAll() { rootTask = null - status.postValue(InitialSyncProgressService.Status.Idle) + status.postValue(SyncStatusService.Status.Idle) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt similarity index 95% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt index c9d7ad21938c960dee8debfb7e6efc8c1d15cdaa..079fd1d3e5d2ba5811c3cc6aa1696463833ab7f8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt @@ -33,8 +33,8 @@ import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams import javax.inject.Inject -internal class AddHttpPusherWorker(context: Context, params: WorkerParameters) - : SessionSafeCoroutineWorker<AddHttpPusherWorker.Params>(context, params, Params::class.java) { +internal class AddPusherWorker(context: Context, params: WorkerParameters) + : SessionSafeCoroutineWorker<AddPusherWorker.Params>(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt index a772cf5ebb17ceb97c86f85174f00d23864ebec1..9a50abfe3572e3510da9da2dd9a30c0216f1b081 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt @@ -66,27 +66,45 @@ internal class DefaultPushersService @Inject constructor( deviceDisplayName: String, url: String, append: Boolean, - withEventIdOnly: Boolean) - : UUID { - // Do some parameter checks. It's ok to throw Exception, to inform developer of the problem - if (pushkey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars") - if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars") - if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") + withEventIdOnly: Boolean + ) = addPusher( + JsonPusher( + pushKey = pushkey, + kind = Pusher.KIND_HTTP, + appId = appId, + profileTag = profileTag, + lang = lang, + appDisplayName = appDisplayName, + deviceDisplayName = deviceDisplayName, + data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }), + append = append + ) + ) - val pusher = JsonPusher( - pushKey = pushkey, - kind = "http", - appId = appId, - appDisplayName = appDisplayName, - deviceDisplayName = deviceDisplayName, - profileTag = profileTag, - lang = lang, - data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }), - append = append) + override fun addEmailPusher(email: String, + lang: String, + emailBranding: String, + appDisplayName: String, + deviceDisplayName: String, + append: Boolean + ) = addPusher( + JsonPusher( + pushKey = email, + kind = Pusher.KIND_EMAIL, + appId = Pusher.APP_ID_EMAIL, + profileTag = "", + lang = lang, + appDisplayName = appDisplayName, + deviceDisplayName = deviceDisplayName, + data = JsonPusherData(brand = emailBranding), + append = append + ) + ) - val params = AddHttpPusherWorker.Params(sessionId, pusher) - - val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>() + private fun addPusher(pusher: JsonPusher): UUID { + pusher.validateParameters() + val params = AddPusherWorker.Params(sessionId, pusher) + val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddPusherWorker>() .setConstraints(WorkManagerProvider.workConstraints) .setInputData(WorkerParamsFactory.toData(params)) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS) @@ -95,8 +113,27 @@ internal class DefaultPushersService @Inject constructor( return request.id } + private fun JsonPusher.validateParameters() { + // Do some parameter checks. It's ok to throw Exception, to inform developer of the problem + if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars") + if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars") + data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") } + } + + override suspend fun removePusher(pusher: Pusher) { + removePusher(pusher.pushKey, pusher.appId) + } + override suspend fun removeHttpPusher(pushkey: String, appId: String) { - val params = RemovePusherTask.Params(pushkey, appId) + removePusher(pushkey, appId) + } + + override suspend fun removeEmailPusher(email: String) { + removePusher(pushKey = email, Pusher.APP_ID_EMAIL) + } + + private suspend fun removePusher(pushKey: String, pushAppId: String) { + val params = RemovePusherTask.Params(pushKey, pushAppId) removePusherTask.execute(params) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusherData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusherData.kt index c8d4d77fb1ae27903b0bf1723bcce0d8b66629b7..42a8fa6ff3912cb6b9cee910a788f9b0b9535aaa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusherData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusherData.kt @@ -32,5 +32,8 @@ internal data class JsonPusherData( * Currently the only format available is 'event_id_only'. */ @Json(name = "format") - val format: String? = null + val format: String? = null, + + @Json(name = "brand") + val brand: String? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/GetRoomSummaryTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/GetRoomSummaryTask.kt new file mode 100644 index 0000000000000000000000000000000000000000..d9547d9e3a76c8c3e3f87d0976cc05743ecbe1cc --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/GetRoomSummaryTask.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room + +import org.matrix.android.sdk.api.session.room.model.RoomStrippedState +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface GetRoomSummaryTask : Task<GetRoomSummaryTask.Params, RoomStrippedState> { + data class Params( + val roomId: String, + val viaServers: List<String>? + ) +} + +internal class DefaultGetRoomSummaryTask @Inject constructor( + private val roomAPI: RoomAPI, + private val globalErrorReceiver: GlobalErrorReceiver +) : GetRoomSummaryTask { + + override suspend fun execute(params: GetRoomSummaryTask.Params): RoomStrippedState { + return executeRequest(globalErrorReceiver) { + roomAPI.getRoomSummary(params.roomId, params.viaServers) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 535fa9df716d4bcfb527d0aba77cd2fc05829444..98e76592382d5c5a51302db4a655c0cf2ceb4180 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomStrippedState import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse import org.matrix.android.sdk.api.util.JsonDict @@ -254,7 +255,7 @@ internal interface RoomAPI { @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "join/{roomIdOrAlias}") suspend fun join(@Path("roomIdOrAlias") roomIdOrAlias: String, @Query("server_name") viaServers: List<String>, - @Body params: JsonDict): JoinRoomResponse + @Body params: JsonDict): JoinRoomResponse /** * Leave the given room. @@ -381,4 +382,14 @@ internal interface RoomAPI { @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/upgrade") suspend fun upgradeRoom(@Path("roomId") roomId: String, @Body body: RoomUpgradeBody): RoomUpgradeResponse + + /** + * The API returns the summary of the specified room, if the room could be found and the client should be able to view + * its contents according to the join_rules, history visibility, space membership and similar rules outlined in MSC3173 + * as well as if the user is already a member of that room. + * https://github.com/deepbluev7/matrix-doc/blob/room-summaries/proposals/3266-room-summary.md + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "im.nheko.summary/rooms/{roomIdOrAlias}/summary") + suspend fun getRoomSummary(@Path("roomIdOrAlias") roomidOrAlias: String, + @Query("via") viaServers: List<String>?): RoomStrippedState } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 794970705cd02a1902aa06ac781ce1fc572ee236..dbd0ae6f060a1e011af0207de53bb023eb0aeb5e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -253,4 +253,7 @@ internal abstract class RoomModule { @Binds abstract fun bindSign3pidInvitationTask(task: DefaultSign3pidInvitationTask): Sign3pidInvitationTask + + @Binds + abstract fun bindGetRoomSummaryTask(task: DefaultGetRoomSummaryTask): GetRoomSummaryTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt index 219e9c903faecd05733e902cde31951637966141..63fc26e9d6d8e502b95f68155fb573b3860e1ec2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFi import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.peeking.PeekResult import org.matrix.android.sdk.api.util.MatrixItem +import org.matrix.android.sdk.internal.session.room.GetRoomSummaryTask import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask @@ -49,6 +50,7 @@ internal class DefaultPeekRoomTask @Inject constructor( private val getRoomIdByAliasTask: GetRoomIdByAliasTask, private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask, private val getPublicRoomTask: GetPublicRoomTask, + private val getRoomSummaryTask: GetRoomSummaryTask, private val resolveRoomStateTask: ResolveRoomStateTask ) : PeekRoomTask { @@ -70,6 +72,25 @@ internal class DefaultPeekRoomTask @Inject constructor( serverList = emptyList() } + // If the room summary API is available on the Home Server we should try it first + val strippedState = tryOrNull("Failed to get room stripped state roomId:$roomId") { + getRoomSummaryTask.execute(GetRoomSummaryTask.Params(roomId, serverList)) + } + if (strippedState != null) { + return PeekResult.Success( + roomId = strippedState.roomId, + alias = strippedState.getPrimaryAlias() ?: params.roomIdOrAlias.takeIf { isAlias }, + avatarUrl = strippedState.avatarUrl, + name = strippedState.name, + topic = strippedState.topic, + numJoinedMembers = strippedState.numJoinedMembers, + viaServers = serverList, + roomType = strippedState.roomType, + someMembers = null, + isPublic = strippedState.worldReadable + ) + } + // Is it a public room? val visibilityRes = tryOrNull("## PEEK: failed to get visibility") { getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId)) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 89a3533946d02b3f5ed91669f07088271e974ae9..4a6e27b7c0b07973b03cd5fa0d0b384ab6253c8d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent @@ -31,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomTopicContent import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.VersioningState import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent +import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.crypto.EventDecryptor import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM @@ -207,63 +209,102 @@ internal class RoomSummaryUpdater @Inject constructor( } .toMap() - lookupMap.keys.forEach { lookedUp -> - if (lookedUp.roomType == RoomType.SPACE) { - // get childrens + // First handle child relations + lookupMap.keys.asSequence() + .filter { it.roomType == RoomType.SPACE } + .forEach { lookedUp -> + // get childrens - lookedUp.children.clearWith { it.deleteFromRealm() } + lookedUp.children.clearWith { it.deleteFromRealm() } - RoomChildRelationInfo(realm, lookedUp.roomId).getDirectChildrenDescriptions().forEach { child -> + RoomChildRelationInfo(realm, lookedUp.roomId).getDirectChildrenDescriptions().forEach { child -> - lookedUp.children.add( - realm.createObject<SpaceChildSummaryEntity>().apply { - this.childRoomId = child.roomId - this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst() - this.order = child.order + lookedUp.children.add( + realm.createObject<SpaceChildSummaryEntity>().apply { + this.childRoomId = child.roomId + this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst() + this.order = child.order // this.autoJoin = child.autoJoin - this.viaServers.addAll(child.viaServers) - } - ) - - RoomSummaryEntity.where(realm, child.roomId) - .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) - .findFirst() - ?.let { childSum -> - lookupMap.entries.firstOrNull { it.key.roomId == lookedUp.roomId }?.let { entry -> - if (entry.value.indexOfFirst { it.roomId == childSum.roomId } == -1) { - // add looked up as a parent - entry.value.add(childSum) + this.viaServers.addAll(child.viaServers) + } + ) + + RoomSummaryEntity.where(realm, child.roomId) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) + .findFirst() + ?.let { childSum -> + lookupMap.entries.firstOrNull { it.key.roomId == lookedUp.roomId }?.let { entry -> + if (entry.value.indexOfFirst { it.roomId == childSum.roomId } == -1) { + // add looked up as a parent + entry.value.add(childSum) + } } } + } + } + + // Now let's check parent relations + + lookupMap.keys + .forEach { lookedUp -> + lookedUp.parents.clearWith { it.deleteFromRealm() } + // can we check parent relations here?? + /** + * rooms can claim parents via the m.space.parent state event. + * canonical determines whether this is the main parent for the space. + * + * To avoid abuse where a room admin falsely claims that a room is part of a space that it should not be, + * clients could ignore such m.space.parent events unless either + * (a) there is a corresponding m.space.child event in the claimed parent, or + * (b) the sender of the m.space.child event has a sufficient power-level to send such an m.space.child event in the parent. + * (It is not necessarily required that that user currently be a member of the parent room - + * only the m.room.power_levels event is inspected.) + * [Checking the power-level rather than requiring an actual m.space.child event in the parent allows for "secret" rooms (see below).] + */ + RoomChildRelationInfo(realm, lookedUp.roomId).getParentDescriptions() + .map { parentInfo -> + // Is it a valid parent relation? + // Check if it's a child of the parent? + val isValidRelation: Boolean + val parent = lookupMap.firstNotNullOfOrNull { if (it.key.roomId == parentInfo.roomId) it.value else null } + if (parent?.firstOrNull { it.roomId == lookedUp.roomId } != null) { + // there is a corresponding m.space.child event in the claimed parent + isValidRelation = true + } else { + // check if sender can post child relation in parent? + val senderId = parentInfo.stateEventSender + val parentRoomId = parentInfo.roomId + val powerLevelsHelper = CurrentStateEventEntity + .getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_POWER_LEVELS) + ?.root + ?.let { ContentMapper.map(it.content).toModel<PowerLevelsContent>() } + ?.let { PowerLevelsHelper(it) } + + isValidRelation = powerLevelsHelper?.isUserAllowedToSend(senderId, true, EventType.STATE_SPACE_CHILD) ?: false + } + + if (isValidRelation) { + lookedUp.parents.add( + realm.createObject<SpaceParentSummaryEntity>().apply { + this.parentRoomId = parentInfo.roomId + this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst() + this.canonical = parentInfo.canonical + this.viaServers.addAll(parentInfo.viaServers) + } + ) + + RoomSummaryEntity.where(realm, parentInfo.roomId) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) + .findFirst() + ?.let { parentSum -> + if (lookupMap[parentSum]?.indexOfFirst { it.roomId == lookedUp.roomId } == -1) { + // add lookedup as a parent + lookupMap[parentSum]?.add(lookedUp) + } + } + } } } - } else { - lookedUp.parents.clearWith { it.deleteFromRealm() } - // can we check parent relations here?? - RoomChildRelationInfo(realm, lookedUp.roomId).getParentDescriptions() - .map { parentInfo -> - - lookedUp.parents.add( - realm.createObject<SpaceParentSummaryEntity>().apply { - this.parentRoomId = parentInfo.roomId - this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst() - this.canonical = parentInfo.canonical - this.viaServers.addAll(parentInfo.viaServers) - } - ) - - RoomSummaryEntity.where(realm, parentInfo.roomId) - .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) - .findFirst() - ?.let { parentSum -> - if (lookupMap[parentSum]?.indexOfFirst { it.roomId == lookedUp.roomId } == -1) { - // add lookedup as a parent - lookupMap[parentSum]?.add(lookedUp) - } - } - } - } - } // Simple algorithm to break cycles // Need more work to decide how to break, probably need to be as consistent as possible diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt index 8a6bbc18fd1cb33545fdbfc7d83105552ba93fce..8589db27b1579ffa3f2644e8432aa4528afa8c38 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt @@ -89,7 +89,6 @@ internal class DefaultSpace( body = SpaceChildContent( order = null, via = null, -// autoJoin = null, suggested = null ).toContent() ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index 7be4cdcda954f5c0ebd38547e794b74084a8f32a..ac20c7905878305441ec4a6f9d9bb08c95a4c391 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.space.JoinSpaceResult import org.matrix.android.sdk.api.session.space.Space @@ -77,7 +78,7 @@ internal class DefaultSpaceService @Inject constructor( if (isPublic) { this.roomAliasName = roomAliasLocalPart this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( - invite = 0 + invite = if (isPublic) Role.Default.value else Role.Moderator.value ) this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE @@ -221,4 +222,23 @@ internal class DefaultSpaceService @Inject constructor( ).toContent() ) } + + override suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String) { + val room = roomGetter.getRoom(childRoomId) + ?: throw IllegalArgumentException("Unknown Room $childRoomId") + + val existingEvent = room.getStateEvent(EventType.STATE_SPACE_PARENT, QueryStringValue.Equals(parentSpaceId)) + if (existingEvent != null) { + // Should i check if it was sent by me? + // we don't check power level, it will throw if you cannot do that + room.sendStateEvent( + eventType = EventType.STATE_SPACE_PARENT, + stateKey = parentSpaceId, + body = SpaceParentContent( + via = null, + canonical = null + ).toContent() + ) + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index c80fbe60c1b7e52d3f856a27645bde819021e262..df3d8492c3682743ef91a8c88459832011d52dbd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -17,7 +17,9 @@ package org.matrix.android.sdk.internal.session.sync import okhttp3.ResponseBody +import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.initsync.InitSyncStep +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.internal.di.SessionFilesDirectory import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.GlobalErrorReceiver @@ -26,7 +28,7 @@ import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.toFailure import org.matrix.android.sdk.internal.session.filter.FilterRepository import org.matrix.android.sdk.internal.session.homeserver.GetHomeServerCapabilitiesTask -import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService +import org.matrix.android.sdk.internal.session.initsync.DefaultSyncStatusService import org.matrix.android.sdk.internal.session.initsync.reportSubtask import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral import org.matrix.android.sdk.internal.session.sync.parsing.InitialSyncResponseParser @@ -40,6 +42,8 @@ import java.io.File import java.net.SocketTimeoutException import javax.inject.Inject +private val loggerTag = LoggerTag("SyncTask", LoggerTag.SYNC) + internal interface SyncTask : Task<SyncTask.Params, Unit> { data class Params( @@ -53,7 +57,7 @@ internal class DefaultSyncTask @Inject constructor( @UserId private val userId: String, private val filterRepository: FilterRepository, private val syncResponseHandler: SyncResponseHandler, - private val initialSyncProgressService: DefaultInitialSyncProgressService, + private val defaultSyncStatusService: DefaultSyncStatusService, private val syncTokenStore: SyncTokenStore, private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask, private val userStore: UserStore, @@ -75,7 +79,7 @@ internal class DefaultSyncTask @Inject constructor( } private suspend fun doSync(params: SyncTask.Params) { - Timber.v("Sync task started on Thread: ${Thread.currentThread().name}") + Timber.tag(loggerTag.value).d("Sync task started on Thread: ${Thread.currentThread().name}") val requestParams = HashMap<String, String>() var timeout = 0L @@ -92,7 +96,7 @@ internal class DefaultSyncTask @Inject constructor( if (isInitialSync) { // We might want to get the user information in parallel too userStore.createOrUpdate(userId) - initialSyncProgressService.startRoot(InitSyncStep.ImportingAccount, 100) + defaultSyncStatusService.startRoot(InitSyncStep.ImportingAccount, 100) } // Maybe refresh the homeserver capabilities data we know getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = false)) @@ -100,20 +104,20 @@ internal class DefaultSyncTask @Inject constructor( val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT) if (isInitialSync) { - Timber.d("INIT_SYNC with filter: ${requestParams["filter"]}") + Timber.tag(loggerTag.value).d("INIT_SYNC with filter: ${requestParams["filter"]}") val initSyncStrategy = initialSyncStrategy - logDuration("INIT_SYNC strategy: $initSyncStrategy") { + logDuration("INIT_SYNC strategy: $initSyncStrategy", loggerTag) { if (initSyncStrategy is InitialSyncStrategy.Optimized) { roomSyncEphemeralTemporaryStore.reset() workingDir.mkdirs() val file = downloadInitSyncResponse(requestParams) - reportSubtask(initialSyncProgressService, InitSyncStep.ImportingAccount, 1, 0.7F) { + reportSubtask(defaultSyncStatusService, InitSyncStep.ImportingAccount, 1, 0.7F) { handleSyncFile(file, initSyncStrategy) } // Delete all files workingDir.deleteRecursively() } else { - val syncResponse = logDuration("INIT_SYNC Request") { + val syncResponse = logDuration("INIT_SYNC Request", loggerTag) { executeRequest(globalErrorReceiver) { syncAPI.sync( params = requestParams, @@ -122,43 +126,60 @@ internal class DefaultSyncTask @Inject constructor( } } - logDuration("INIT_SYNC Database insertion") { - syncResponseHandler.handleResponse(syncResponse, token, initialSyncProgressService) + logDuration("INIT_SYNC Database insertion", loggerTag) { + syncResponseHandler.handleResponse(syncResponse, token, defaultSyncStatusService) } } } - initialSyncProgressService.endAll() + defaultSyncStatusService.endAll() } else { - val syncResponse = executeRequest(globalErrorReceiver) { - syncAPI.sync( - params = requestParams, - readTimeOut = readTimeOut - ) + Timber.tag(loggerTag.value).d("Start incremental sync request") + defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncIdle) + val syncResponse = try { + executeRequest(globalErrorReceiver) { + syncAPI.sync( + params = requestParams, + readTimeOut = readTimeOut + ) + } + } catch (throwable: Throwable) { + Timber.tag(loggerTag.value).e(throwable, "Incremental sync request error") + defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncError) + throw throwable } + val nbRooms = syncResponse.rooms?.invite.orEmpty().size + syncResponse.rooms?.join.orEmpty().size + syncResponse.rooms?.leave.orEmpty().size + val nbToDevice = syncResponse.toDevice?.events.orEmpty().size + Timber.tag(loggerTag.value).d("Incremental sync request parsing, $nbRooms room(s) $nbToDevice toDevice(s)") + defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncParsing( + rooms = nbRooms, + toDevice = nbToDevice + )) syncResponseHandler.handleResponse(syncResponse, token, null) + Timber.tag(loggerTag.value).d("Incremental sync done") + defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncDone) } - Timber.v("Sync task finished on Thread: ${Thread.currentThread().name}") + Timber.tag(loggerTag.value).d("Sync task finished on Thread: ${Thread.currentThread().name}") } private suspend fun downloadInitSyncResponse(requestParams: Map<String, String>): File { val workingFile = File(workingDir, "initSync.json") val status = initialSyncStatusRepository.getStep() if (workingFile.exists() && status >= InitialSyncStatus.STEP_DOWNLOADED) { - Timber.d("INIT_SYNC file is already here") - reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.3f) { + Timber.tag(loggerTag.value).d("INIT_SYNC file is already here") + reportSubtask(defaultSyncStatusService, InitSyncStep.Downloading, 1, 0.3f) { // Empty task } } else { initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADING) - val syncResponse = logDuration("INIT_SYNC Perform server request") { - reportSubtask(initialSyncProgressService, InitSyncStep.ServerComputing, 1, 0.2f) { + val syncResponse = logDuration("INIT_SYNC Perform server request", loggerTag) { + reportSubtask(defaultSyncStatusService, InitSyncStep.ServerComputing, 1, 0.2f) { getSyncResponse(requestParams, MAX_NUMBER_OF_RETRY_AFTER_TIMEOUT) } } if (syncResponse.isSuccessful) { - logDuration("INIT_SYNC Download and save to file") { - reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.1f) { + logDuration("INIT_SYNC Download and save to file", loggerTag) { + reportSubtask(defaultSyncStatusService, InitSyncStep.Downloading, 1, 0.1f) { syncResponse.body()?.byteStream()?.use { inputStream -> workingFile.outputStream().use { outputStream -> inputStream.copyTo(outputStream) @@ -168,7 +189,7 @@ internal class DefaultSyncTask @Inject constructor( } } else { throw syncResponse.toFailure(globalErrorReceiver) - .also { Timber.w("INIT_SYNC request failure: $this") } + .also { Timber.tag(loggerTag.value).w("INIT_SYNC request failure: $this") } } initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADED) } @@ -185,9 +206,9 @@ internal class DefaultSyncTask @Inject constructor( ).awaitResponse() } catch (throwable: Throwable) { if (throwable is SocketTimeoutException && retry > 0) { - Timber.w("INIT_SYNC timeout retry left: $retry") + Timber.tag(loggerTag.value).w("INIT_SYNC timeout retry left: $retry") } else { - Timber.e(throwable, "INIT_SYNC timeout, no retry left, or other error") + Timber.tag(loggerTag.value).e(throwable, "INIT_SYNC timeout, no retry left, or other error") throw throwable } } @@ -195,18 +216,18 @@ internal class DefaultSyncTask @Inject constructor( } private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized) { - logDuration("INIT_SYNC handleSyncFile()") { - val syncResponse = logDuration("INIT_SYNC Read file and parse") { + logDuration("INIT_SYNC handleSyncFile()", loggerTag) { + val syncResponse = logDuration("INIT_SYNC Read file and parse", loggerTag) { syncResponseParser.parse(initSyncStrategy, workingFile) } initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_PARSED) // Log some stats val nbOfJoinedRooms = syncResponse.rooms?.join?.size ?: 0 val nbOfJoinedRoomsInFile = syncResponse.rooms?.join?.values?.count { it.ephemeral is LazyRoomSyncEphemeral.Stored } - Timber.d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files") + Timber.tag(loggerTag.value).d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files") - logDuration("INIT_SYNC Database insertion") { - syncResponseHandler.handleResponse(syncResponse, null, initialSyncProgressService) + logDuration("INIT_SYNC Database insertion", loggerTag) { + syncResponseHandler.handleResponse(syncResponse, null, defaultSyncStatusService) } initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_SUCCESS) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt index de8d009892fd86d793b29274e2d4b82a8e05d6f7..b3a6cafb7d45edc2619ee2f1142e9460603acfc6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt @@ -36,6 +36,7 @@ import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.internal.session.call.ActiveCallHandler import org.matrix.android.sdk.internal.session.sync.SyncPresence @@ -49,6 +50,8 @@ import kotlin.concurrent.schedule private const val RETRY_WAIT_TIME_MS = 10_000L private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L +private val loggerTag = LoggerTag("SyncThread", LoggerTag.SYNC) + internal class SyncThread @Inject constructor(private val syncTask: SyncTask, private val networkConnectivityChecker: NetworkConnectivityChecker, private val backgroundDetectionObserver: BackgroundDetectionObserver, @@ -83,7 +86,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, fun restart() = synchronized(lock) { if (!isStarted) { - Timber.v("Resume sync...") + Timber.tag(loggerTag.value).d("Resume sync...") isStarted = true // Check again server availability and the token validity canReachServer = true @@ -94,7 +97,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, fun pause() = synchronized(lock) { if (isStarted) { - Timber.v("Pause sync...") + Timber.tag(loggerTag.value).d("Pause sync...") isStarted = false retryNoNetworkTask?.cancel() syncScope.coroutineContext.cancelChildren() @@ -102,7 +105,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, } fun kill() = synchronized(lock) { - Timber.v("Kill sync...") + Timber.tag(loggerTag.value).d("Kill sync...") updateStateTo(SyncState.Killing) retryNoNetworkTask?.cancel() syncScope.coroutineContext.cancelChildren() @@ -124,21 +127,21 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, } override fun run() { - Timber.v("Start syncing...") + Timber.tag(loggerTag.value).d("Start syncing...") isStarted = true networkConnectivityChecker.register(this) backgroundDetectionObserver.register(this) registerActiveCallsObserver() while (state != SyncState.Killing) { - Timber.v("Entering loop, state: $state") + Timber.tag(loggerTag.value).d("Entering loop, state: $state") if (!isStarted) { - Timber.v("Sync is Paused. Waiting...") + Timber.tag(loggerTag.value).d("Sync is Paused. Waiting...") updateStateTo(SyncState.Paused) synchronized(lock) { lock.wait() } - Timber.v("...unlocked") + Timber.tag(loggerTag.value).d("...unlocked") } else if (!canReachServer) { - Timber.v("No network. Waiting...") + Timber.tag(loggerTag.value).d("No network. Waiting...") updateStateTo(SyncState.NoNetwork) // We force retrying in RETRY_WAIT_TIME_MS maximum. Otherwise it will be unlocked by onConnectivityChanged() or restart() retryNoNetworkTask = Timer(SyncState.NoNetwork.toString(), false).schedule(RETRY_WAIT_TIME_MS) { @@ -148,19 +151,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, } } synchronized(lock) { lock.wait() } - Timber.v("...retry") + Timber.tag(loggerTag.value).d("...retry") } else if (!isTokenValid) { - Timber.v("Token is invalid. Waiting...") + Timber.tag(loggerTag.value).d("Token is invalid. Waiting...") updateStateTo(SyncState.InvalidToken) synchronized(lock) { lock.wait() } - Timber.v("...unlocked") + Timber.tag(loggerTag.value).d("...unlocked") } else { if (state !is SyncState.Running) { updateStateTo(SyncState.Running(afterPause = true)) } // No timeout after a pause val timeout = state.let { if (it is SyncState.Running && it.afterPause) 0 else DEFAULT_LONG_POOL_TIMEOUT } - Timber.v("Execute sync request with timeout $timeout") + Timber.tag(loggerTag.value).d("Execute sync request with timeout $timeout") val params = SyncTask.Params(timeout, SyncPresence.Online) val sync = syncScope.launch { doSync(params) @@ -168,10 +171,10 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, runBlocking { sync.join() } - Timber.v("...Continue") + Timber.tag(loggerTag.value).d("...Continue") } } - Timber.v("Sync killed") + Timber.tag(loggerTag.value).d("Sync killed") updateStateTo(SyncState.Killed) backgroundDetectionObserver.unregister(this) networkConnectivityChecker.unregister(this) @@ -199,19 +202,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, } if (failure is Failure.NetworkConnection && failure.cause is SocketTimeoutException) { // Timeout are not critical - Timber.v("Timeout") + Timber.tag(loggerTag.value).d("Timeout") } else if (failure is CancellationException) { - Timber.v("Cancelled") + Timber.tag(loggerTag.value).d("Cancelled") } else if (failure.isTokenError()) { // No token or invalid token, stop the thread - Timber.w(failure, "Token error") + Timber.tag(loggerTag.value).w(failure, "Token error") isStarted = false isTokenValid = false } else { - Timber.e(failure) + Timber.tag(loggerTag.value).e(failure) if (failure !is Failure.NetworkConnection || failure.cause is JsonEncodingException) { // Wait 10s before retrying - Timber.v("Wait 10s") + Timber.tag(loggerTag.value).d("Wait 10s") delay(RETRY_WAIT_TIME_MS) } } @@ -225,7 +228,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, } private fun updateStateTo(newState: SyncState) { - Timber.v("Update state from $state to $newState") + Timber.tag(loggerTag.value).d("Update state from $state to $newState") if (newState == state) { return } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt index 4656856bf788d50ecda8e47cecad840d65ab0a5f..6fd907d397235cb45fe8981c64eb9659480b9108 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.util import org.matrix.android.sdk.BuildConfig +import org.matrix.android.sdk.api.logger.LoggerTag import timber.log.Timber internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String { @@ -32,14 +33,15 @@ internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String { } internal suspend fun <T> logDuration(message: String, + loggerTag: LoggerTag, block: suspend () -> T): T { - Timber.d("$message -- BEGIN") + Timber.tag(loggerTag.value).d("$message -- BEGIN") val start = System.currentTimeMillis() val result = logRamUsage(message) { block() } val duration = System.currentTimeMillis() - start - Timber.d("$message -- END duration: $duration ms") + Timber.tag(loggerTag.value).d("$message -- END duration: $duration ms") return result } diff --git a/tools/import_from_element.sh b/tools/import_from_element.sh index 8d64b6db6b3ff6ae5d68d5b2c2b3c57793ec2128..f495c92599eb7913028b8b459d3b51db82387a6d 100755 --- a/tools/import_from_element.sh +++ b/tools/import_from_element.sh @@ -36,6 +36,9 @@ echo "Importing matrix-sdk-android..." rm -rf ./matrix-sdk-android cp -r ${elementAndroidPath}/matrix-sdk-android . +# Copy other files +cp -r ${elementAndroidPath}/dependencies.gradle . + # Add all changes to git git add -A