From f7d9665eafdc8270eaec3e44b8840ed216ebad46 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Jan 2023 17:57:34 +0100 Subject: [PATCH 1/3] Add some refactoring and first simple test on RoomListPresenter --- app/build.gradle.kts | 2 + .../element/android/x/ElementXApplication.kt | 2 +- .../java/io/element/android/x/MainActivity.kt | 4 +- .../io/element/android/x/di/AppBindings.kt | 4 +- .../java/io/element/android/x/di/AppModule.kt | 7 ++ .../android/x/node/LoggedInFlowNode.kt | 2 +- .../io/element/android/x/node/RoomFlowNode.kt | 2 +- .../io/element/android/x/node/RootFlowNode.kt | 10 +- build.gradle.kts | 1 - .../changeserver/ChangeServerPresenter.kt | 8 +- .../features/login/root/LoginRootPresenter.kt | 12 +-- features/roomlist/build.gradle.kts | 11 +- .../features/roomlist/model/RoomListState.kt | 2 +- .../roomlist/RoomListPresenterTests.kt | 43 ++++++++ gradle/libs.versions.toml | 6 +- libraries/architecture/build.gradle.kts | 6 +- .../android/x/architecture/Bindings.kt | 5 +- .../android/x}/di/DaggerComponentOwner.kt | 2 +- .../android/x/matrix/FakeMatrixClient.kt | 69 ++++++++++++ .../android/x/matrix/RustMatrixClient.kt | 1 + .../auth/MatrixAuthenticationService.kt | 31 ++++++ .../RustMatrixAuthenticationService.kt} | 38 ++++--- .../android/x/matrix/di/MatrixModule.kt | 36 +++++++ .../x/matrix/media/FakeMediaResolver.kt | 21 ++-- .../android/x/matrix/room/FakeMatrixRoom.kt | 66 ++++++++++++ .../room/InMemoryRoomSummaryDataSource.kt | 29 +++++ .../matrix/session/PreferencesSessionStore.kt | 100 ++++++++++++++++++ .../android/x/matrix/session/SessionStore.kt | 78 ++------------ .../x/matrix/timeline/FakeMatrixTimeline.kt | 60 +++++++++++ .../java/extension/DependencyHandleScope.kt | 2 - 30 files changed, 520 insertions(+), 140 deletions(-) create mode 100644 features/roomlist/src/test/java/io/element/android/x/features/roomlist/RoomListPresenterTests.kt rename libraries/{core/src/main/java/io/element/android/x/core => di/src/main/java/io/element/android/x}/di/DaggerComponentOwner.kt (95%) create mode 100644 libraries/matrix/src/main/java/io/element/android/x/matrix/FakeMatrixClient.kt create mode 100644 libraries/matrix/src/main/java/io/element/android/x/matrix/auth/MatrixAuthenticationService.kt rename libraries/matrix/src/main/java/io/element/android/x/matrix/{Matrix.kt => auth/RustMatrixAuthenticationService.kt} (73%) create mode 100644 libraries/matrix/src/main/java/io/element/android/x/matrix/di/MatrixModule.kt rename features/roomlist/src/test/java/io/element/android/x/features/roomlist/ExampleUnitTest.kt => libraries/matrix/src/main/java/io/element/android/x/matrix/media/FakeMediaResolver.kt (59%) create mode 100644 libraries/matrix/src/main/java/io/element/android/x/matrix/room/FakeMatrixRoom.kt create mode 100644 libraries/matrix/src/main/java/io/element/android/x/matrix/room/InMemoryRoomSummaryDataSource.kt create mode 100644 libraries/matrix/src/main/java/io/element/android/x/matrix/session/PreferencesSessionStore.kt create mode 100644 libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/FakeMatrixTimeline.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 220835ffa4..7674c05ffa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -32,6 +32,8 @@ plugins { android { namespace = "io.element.android.x" + testOptions { unitTests.isIncludeAndroidResources = true } + defaultConfig { applicationId = "io.element.android.x" targetSdk = 33 // TODO Use Versions.targetSdk diff --git a/app/src/main/java/io/element/android/x/ElementXApplication.kt b/app/src/main/java/io/element/android/x/ElementXApplication.kt index 285c56d5d8..4532276e11 100644 --- a/app/src/main/java/io/element/android/x/ElementXApplication.kt +++ b/app/src/main/java/io/element/android/x/ElementXApplication.kt @@ -18,7 +18,7 @@ package io.element.android.x import android.app.Application import androidx.startup.AppInitializer -import io.element.android.x.core.di.DaggerComponentOwner +import io.element.android.x.di.DaggerComponentOwner import io.element.android.x.di.AppComponent import io.element.android.x.di.DaggerAppComponent import io.element.android.x.initializer.CrashInitializer diff --git a/app/src/main/java/io/element/android/x/MainActivity.kt b/app/src/main/java/io/element/android/x/MainActivity.kt index 3f9ff827cc..2630c78c7e 100644 --- a/app/src/main/java/io/element/android/x/MainActivity.kt +++ b/app/src/main/java/io/element/android/x/MainActivity.kt @@ -26,7 +26,7 @@ import androidx.core.view.WindowCompat import com.bumble.appyx.core.integration.NodeHost import com.bumble.appyx.core.integrationpoint.NodeComponentActivity import io.element.android.x.architecture.bindings -import io.element.android.x.core.di.DaggerComponentOwner +import io.element.android.x.di.DaggerComponentOwner import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.di.AppBindings import io.element.android.x.node.RootFlowNode @@ -47,7 +47,7 @@ class MainActivity : NodeComponentActivity() { RootFlowNode( buildContext = it, appComponentOwner = applicationContext as DaggerComponentOwner, - matrix = appBindings.matrix(), + authenticationService = appBindings.authenticationService(), rootPresenter = appBindings.rootPresenter() ) } diff --git a/app/src/main/java/io/element/android/x/di/AppBindings.kt b/app/src/main/java/io/element/android/x/di/AppBindings.kt index 8cebc3e838..78f40f0322 100644 --- a/app/src/main/java/io/element/android/x/di/AppBindings.kt +++ b/app/src/main/java/io/element/android/x/di/AppBindings.kt @@ -17,7 +17,7 @@ package io.element.android.x.di import com.squareup.anvil.annotations.ContributesTo -import io.element.android.x.matrix.Matrix +import io.element.android.x.matrix.auth.MatrixAuthenticationService import io.element.android.x.root.RootPresenter import kotlinx.coroutines.CoroutineScope @@ -25,5 +25,5 @@ import kotlinx.coroutines.CoroutineScope interface AppBindings { fun coroutineScope(): CoroutineScope fun rootPresenter(): RootPresenter - fun matrix(): Matrix + fun authenticationService(): MatrixAuthenticationService } diff --git a/app/src/main/java/io/element/android/x/di/AppModule.kt b/app/src/main/java/io/element/android/x/di/AppModule.kt index 55c331680c..c1b3ff42e7 100644 --- a/app/src/main/java/io/element/android/x/di/AppModule.kt +++ b/app/src/main/java/io/element/android/x/di/AppModule.kt @@ -16,6 +16,7 @@ package io.element.android.x.di +import android.content.Context import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides @@ -26,12 +27,18 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.plus +import java.io.File import java.util.concurrent.Executors @Module @ContributesTo(AppScope::class) object AppModule { + @Provides + fun providesBaseDirectory(@ApplicationContext context: Context): File { + return File(context.filesDir, "sessions") + } + @Provides @SingleIn(AppScope::class) fun providesAppCoroutineScope(): CoroutineScope { diff --git a/app/src/main/java/io/element/android/x/node/LoggedInFlowNode.kt b/app/src/main/java/io/element/android/x/node/LoggedInFlowNode.kt index f3b407953a..8a5a967875 100644 --- a/app/src/main/java/io/element/android/x/node/LoggedInFlowNode.kt +++ b/app/src/main/java/io/element/android/x/node/LoggedInFlowNode.kt @@ -34,7 +34,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import io.element.android.x.architecture.bindings import io.element.android.x.architecture.createNode -import io.element.android.x.core.di.DaggerComponentOwner +import io.element.android.x.di.DaggerComponentOwner import io.element.android.x.di.SessionComponent import io.element.android.x.features.preferences.PreferencesFlowNode import io.element.android.x.features.roomlist.RoomListNode diff --git a/app/src/main/java/io/element/android/x/node/RoomFlowNode.kt b/app/src/main/java/io/element/android/x/node/RoomFlowNode.kt index 21ef4ad9cc..9db5f5cfb5 100644 --- a/app/src/main/java/io/element/android/x/node/RoomFlowNode.kt +++ b/app/src/main/java/io/element/android/x/node/RoomFlowNode.kt @@ -27,7 +27,7 @@ import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.navmodel.backstack.BackStack import io.element.android.x.architecture.bindings import io.element.android.x.architecture.createNode -import io.element.android.x.core.di.DaggerComponentOwner +import io.element.android.x.di.DaggerComponentOwner import io.element.android.x.di.RoomComponent import io.element.android.x.features.messages.MessagesNode import io.element.android.x.matrix.room.MatrixRoom diff --git a/app/src/main/java/io/element/android/x/node/RootFlowNode.kt b/app/src/main/java/io/element/android/x/node/RootFlowNode.kt index 40569bd845..9dee857470 100644 --- a/app/src/main/java/io/element/android/x/node/RootFlowNode.kt +++ b/app/src/main/java/io/element/android/x/node/RootFlowNode.kt @@ -38,10 +38,10 @@ import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import io.element.android.x.architecture.createNode import io.element.android.x.architecture.presenterConnector -import io.element.android.x.core.di.DaggerComponentOwner +import io.element.android.x.di.DaggerComponentOwner import io.element.android.x.features.rageshake.bugreport.BugReportNode -import io.element.android.x.matrix.Matrix import io.element.android.x.matrix.MatrixClient +import io.element.android.x.matrix.auth.MatrixAuthenticationService import io.element.android.x.matrix.core.SessionId import io.element.android.x.root.RootPresenter import io.element.android.x.root.RootView @@ -59,7 +59,7 @@ class RootFlowNode( savedStateMap = buildContext.savedStateMap, ), private val appComponentOwner: DaggerComponentOwner, - private val matrix: Matrix, + private val authenticationService: MatrixAuthenticationService, rootPresenter: RootPresenter ) : ParentNode( @@ -79,12 +79,12 @@ class RootFlowNode( onDestroy = { matrixClientsHolder.remove(child.sessionId) } ) } - matrix.isLoggedIn() + authenticationService.isLoggedIn() .distinctUntilChanged() .onEach { isLoggedIn -> Timber.v("isLoggedIn=$isLoggedIn") if (isLoggedIn) { - val matrixClient = matrix.restoreSession() + val matrixClient = authenticationService.restoreSession() if (matrixClient == null) { backstack.newRoot(NavTarget.NotLoggedInFlow) } else { diff --git a/build.gradle.kts b/build.gradle.kts index eca2828846..fa9822913a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,7 +23,6 @@ plugins { alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.anvil) apply false - alias(libs.plugins.molecule) apply false alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kapt) apply false alias(libs.plugins.dependencycheck) apply false diff --git a/features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerPresenter.kt b/features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerPresenter.kt index ec6f60f2df..13259baf42 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerPresenter.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerPresenter.kt @@ -25,18 +25,18 @@ import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.x.architecture.Async import io.element.android.x.architecture.Presenter import io.element.android.x.architecture.execute -import io.element.android.x.matrix.Matrix +import io.element.android.x.matrix.auth.MatrixAuthenticationService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject -class ChangeServerPresenter @Inject constructor(private val matrix: Matrix) : Presenter { +class ChangeServerPresenter @Inject constructor(private val authenticationService: MatrixAuthenticationService) : Presenter { @Composable override fun present(): ChangeServerState { val localCoroutineScope = rememberCoroutineScope() val homeserver = rememberSaveable { - mutableStateOf(matrix.getHomeserverOrDefault()) + mutableStateOf(authenticationService.getHomeserverOrDefault()) } val changeServerAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) @@ -58,7 +58,7 @@ class ChangeServerPresenter @Inject constructor(private val matrix: Matrix) : Pr private fun CoroutineScope.submit(homeserver: String, changeServerAction: MutableState>) = launch { suspend { - matrix.setHomeserver(homeserver) + authenticationService.setHomeserver(homeserver) }.execute(changeServerAction) } } diff --git a/features/login/src/main/java/io/element/android/x/features/login/root/LoginRootPresenter.kt b/features/login/src/main/java/io/element/android/x/features/login/root/LoginRootPresenter.kt index ef6b2eab7b..e6908dab5b 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/root/LoginRootPresenter.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/root/LoginRootPresenter.kt @@ -23,18 +23,18 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.x.architecture.Presenter -import io.element.android.x.matrix.Matrix +import io.element.android.x.matrix.auth.MatrixAuthenticationService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject -class LoginRootPresenter @Inject constructor(private val matrix: Matrix) : Presenter { +class LoginRootPresenter @Inject constructor(private val authenticationService: MatrixAuthenticationService) : Presenter { @Composable override fun present(): LoginRootState { val localCoroutineScope = rememberCoroutineScope() val homeserver = rememberSaveable { - mutableStateOf(matrix.getHomeserverOrDefault()) + mutableStateOf(authenticationService.getHomeserverOrDefault()) } val loggedInState: MutableState = remember { mutableStateOf(LoggedInState.NotLoggedIn) @@ -67,8 +67,8 @@ class LoginRootPresenter @Inject constructor(private val matrix: Matrix) : Prese private fun CoroutineScope.submit(homeserver: String, formState: LoginFormState, loggedInState: MutableState) = launch { loggedInState.value = LoggedInState.LoggingIn try { - matrix.setHomeserver(homeserver) - val sessionId = matrix.login(formState.login.trim(), formState.password.trim()) + authenticationService.setHomeserver(homeserver) + val sessionId = authenticationService.login(formState.login.trim(), formState.password.trim()) loggedInState.value = LoggedInState.LoggedIn(sessionId) } catch (failure: Throwable) { loggedInState.value = LoggedInState.ErrorLoggingIn(failure) @@ -80,6 +80,6 @@ class LoginRootPresenter @Inject constructor(private val matrix: Matrix) : Prese } private fun refreshHomeServer(homeserver: MutableState) { - homeserver.value = matrix.getHomeserverOrDefault() + homeserver.value = authenticationService.getHomeserverOrDefault() } } diff --git a/features/roomlist/build.gradle.kts b/features/roomlist/build.gradle.kts index 674ec25d88..bc7596ddbf 100644 --- a/features/roomlist/build.gradle.kts +++ b/features/roomlist/build.gradle.kts @@ -31,8 +31,9 @@ anvil { } dependencies { - implementation(project(":anvilannotations")) anvil(project(":anvilcodegen")) + implementation(project(":anvilannotations")) + implementation(project(":libraries:di")) implementation(project(":libraries:core")) implementation(project(":libraries:architecture")) @@ -43,7 +44,15 @@ dependencies { implementation(project(":libraries:elementresources")) implementation(libs.datetime) implementation(libs.accompanist.placeholder) + testImplementation(libs.test.junit) + testImplementation(libs.coroutines.test) + testImplementation(libs.molecule.runtime) + testImplementation(libs.test.truth) + testImplementation(libs.test.turbine) + androidTestImplementation(libs.test.junitext) + ksp(libs.showkase.processor) + } diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListState.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListState.kt index 063f05bc5e..ce2e679fa0 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListState.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListState.kt @@ -26,5 +26,5 @@ data class RoomListState( val roomList: ImmutableList, val filter: String, val isLoginOut: Boolean, - val eventSink: (RoomListEvents) -> Unit = {} + val eventSink: (RoomListEvents) -> Unit ) diff --git a/features/roomlist/src/test/java/io/element/android/x/features/roomlist/RoomListPresenterTests.kt b/features/roomlist/src/test/java/io/element/android/x/features/roomlist/RoomListPresenterTests.kt new file mode 100644 index 0000000000..e2e85e8db3 --- /dev/null +++ b/features/roomlist/src/test/java/io/element/android/x/features/roomlist/RoomListPresenterTests.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.features.roomlist + +import app.cash.molecule.RecompositionClock +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.x.matrix.FakeMatrixClient +import io.element.android.x.matrix.core.SessionId +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class RoomListPresenterTests { + + @Test + fun `present - should start with no user and then load user with success`() = runTest { + + val presenter = RoomListPresenter(FakeMatrixClient(SessionId("sessionId")), LastMessageFormatter()) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.matrixUser).isNull() + val withUserState = awaitItem() + assertThat(withUserState).isNotNull() + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a936ed73c..94e2200209 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,6 +39,7 @@ test_junitext = "1.1.3" test_barista = "4.2.0" test_hamcrest = "2.2" test_orchestrator = "1.4.1" +test_turbine = "0.12.1" #other coil = "2.2.1" @@ -112,6 +113,9 @@ test_mockk = { module = "io.mockk:mockk", version.ref = "test_mockk" } test_barista = { module = "com.adevinta.android:barista", version.ref = "test_barista" } test_hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "test_hamcrest" } test_orchestrator = { module = "androidx.test:orchestrator", version.ref = "test_orchestrator" } +test_turbine = { module = "app.cash.turbine:turbine", version.ref = "test_turbine"} +test_truth = "com.google.truth:truth:1.1.3" + # Others coil = { module = "io.coil-kt:coil", version.ref = "coil" } @@ -124,6 +128,7 @@ showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = {module = "com.bumble.appyx:core", version.ref = "appyx"} +molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } # Di inject = { module = "javax.inject:javax.inject", version = "1" } @@ -147,6 +152,5 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } anvil = { id = "com.squareup.anvil", version.ref = "anvil" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } -molecule = {id = "app.cash.molecule", version.ref = "molecule"} dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.ref = "dependencygraph" } dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" } diff --git a/libraries/architecture/build.gradle.kts b/libraries/architecture/build.gradle.kts index 1bae19e00b..2fad440e85 100644 --- a/libraries/architecture/build.gradle.kts +++ b/libraries/architecture/build.gradle.kts @@ -18,16 +18,16 @@ @Suppress("DSL_SCOPE_VIOLATION") plugins { id("io.element.android-compose-library") - alias(libs.plugins.molecule) } android { - namespace = "io.element.android.x.libraries.presentation" + namespace = "io.element.android.x.libraries.architecture" } dependencies { - api(project(":libraries:core")) + api(project(":libraries:di")) api(libs.dagger) api(libs.appyx.core) + api(libs.molecule.runtime) api(libs.androidx.lifecycle.runtime) } diff --git a/libraries/architecture/src/main/java/io/element/android/x/architecture/Bindings.kt b/libraries/architecture/src/main/java/io/element/android/x/architecture/Bindings.kt index 9844a99896..fadb0764dd 100644 --- a/libraries/architecture/src/main/java/io/element/android/x/architecture/Bindings.kt +++ b/libraries/architecture/src/main/java/io/element/android/x/architecture/Bindings.kt @@ -19,7 +19,6 @@ package io.element.android.x.architecture import android.content.Context import android.content.ContextWrapper import com.bumble.appyx.core.node.Node -import io.element.android.x.core.di.DaggerComponentOwner inline fun Node.bindings() = bindings(T::class.java) inline fun Context.bindings() = bindings(T::class.java) @@ -28,7 +27,7 @@ fun Context.bindings(klass: Class): T { // search dagger components in the context hierarchy return generateSequence(this) { (it as? ContextWrapper)?.baseContext } .plus(applicationContext) - .filterIsInstance() + .filterIsInstance() .map { it.daggerComponent } .flatMap { if (it is Collection<*>) it else listOf(it) } .filterIsInstance(klass) @@ -39,7 +38,7 @@ fun Context.bindings(klass: Class): T { fun Node.bindings(klass: Class): T { // search dagger components in node hierarchy return generateSequence(this, Node::parent) - .filterIsInstance() + .filterIsInstance() .map { it.daggerComponent } .flatMap { if (it is Collection<*>) it else listOf(it) } .filterIsInstance(klass) diff --git a/libraries/core/src/main/java/io/element/android/x/core/di/DaggerComponentOwner.kt b/libraries/di/src/main/java/io/element/android/x/di/DaggerComponentOwner.kt similarity index 95% rename from libraries/core/src/main/java/io/element/android/x/core/di/DaggerComponentOwner.kt rename to libraries/di/src/main/java/io/element/android/x/di/DaggerComponentOwner.kt index 12f51350b8..eaf3371be3 100644 --- a/libraries/core/src/main/java/io/element/android/x/core/di/DaggerComponentOwner.kt +++ b/libraries/di/src/main/java/io/element/android/x/di/DaggerComponentOwner.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.x.core.di +package io.element.android.x.di /** * A [DaggerComponentOwner] is anything that "owns" a Dagger Component. diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/FakeMatrixClient.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/FakeMatrixClient.kt new file mode 100644 index 0000000000..f90540da83 --- /dev/null +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/FakeMatrixClient.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.matrix + +import io.element.android.x.matrix.core.RoomId +import io.element.android.x.matrix.core.SessionId +import io.element.android.x.matrix.core.UserId +import io.element.android.x.matrix.media.FakeMediaResolver +import io.element.android.x.matrix.media.MediaResolver +import io.element.android.x.matrix.room.FakeMatrixRoom +import io.element.android.x.matrix.room.InMemoryRoomSummaryDataSource +import io.element.android.x.matrix.room.MatrixRoom +import io.element.android.x.matrix.room.RoomSummaryDataSource +import org.matrix.rustcomponents.sdk.MediaSource + +class FakeMatrixClient(override val sessionId: SessionId) : MatrixClient { + + override fun getRoom(roomId: RoomId): MatrixRoom? { + return FakeMatrixRoom(roomId) + } + + override fun startSync() = Unit + + override fun stopSync() = Unit + + override fun roomSummaryDataSource(): RoomSummaryDataSource { + return InMemoryRoomSummaryDataSource() + } + + override fun mediaResolver(): MediaResolver { + return FakeMediaResolver() + } + + override suspend fun logout() = Unit + + override fun userId(): UserId = UserId("") + + override suspend fun loadUserDisplayName(): Result { + return Result.success("") + } + + override suspend fun loadUserAvatarURLString(): Result { + return Result.success("") + } + + override suspend fun loadMediaContentForSource(source: MediaSource): Result { + return Result.success(ByteArray(0)) + } + + override suspend fun loadMediaThumbnailForSource(source: MediaSource, width: Long, height: Long): Result { + return Result.success(ByteArray(0)) + } + + override fun close() = Unit +} diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/RustMatrixClient.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/RustMatrixClient.kt index 433b23df04..b96b1e87fb 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/RustMatrixClient.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/RustMatrixClient.kt @@ -26,6 +26,7 @@ import io.element.android.x.matrix.room.MatrixRoom import io.element.android.x.matrix.room.RoomSummaryDataSource import io.element.android.x.matrix.room.RustMatrixRoom import io.element.android.x.matrix.room.RustRoomSummaryDataSource +import io.element.android.x.matrix.session.PreferencesSessionStore import io.element.android.x.matrix.session.SessionStore import io.element.android.x.matrix.session.sessionId import io.element.android.x.matrix.sync.SlidingSyncObserverProxy diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/auth/MatrixAuthenticationService.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/auth/MatrixAuthenticationService.kt new file mode 100644 index 0000000000..f353a4d789 --- /dev/null +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/auth/MatrixAuthenticationService.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.matrix.auth + +import io.element.android.x.matrix.MatrixClient +import io.element.android.x.matrix.core.SessionId +import kotlinx.coroutines.flow.Flow + +interface MatrixAuthenticationService { + fun isLoggedIn(): Flow + suspend fun getLatestSessionId(): SessionId? + suspend fun restoreSession(): MatrixClient? + fun getHomeserver(): String? + fun getHomeserverOrDefault(): String + suspend fun setHomeserver(homeserver: String) + suspend fun login(username: String, password: String): SessionId +} diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/auth/RustMatrixAuthenticationService.kt similarity index 73% rename from libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt rename to libraries/matrix/src/main/java/io/element/android/x/matrix/auth/RustMatrixAuthenticationService.kt index 71c649e13e..3efb6d82c8 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/auth/RustMatrixAuthenticationService.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 2023 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,13 @@ * limitations under the License. */ -package io.element.android.x.matrix +package io.element.android.x.matrix.auth -import android.content.Context +import com.squareup.anvil.annotations.ContributesBinding import io.element.android.x.core.coroutine.CoroutineDispatchers import io.element.android.x.di.AppScope -import io.element.android.x.di.ApplicationContext -import io.element.android.x.di.SingleIn +import io.element.android.x.matrix.MatrixClient +import io.element.android.x.matrix.RustMatrixClient import io.element.android.x.matrix.core.SessionId import io.element.android.x.matrix.session.SessionStore import io.element.android.x.matrix.session.sessionId @@ -35,26 +35,24 @@ import timber.log.Timber import java.io.File import javax.inject.Inject -@SingleIn(AppScope::class) -class Matrix @Inject constructor( +@ContributesBinding(AppScope::class) +class RustMatrixAuthenticationService @Inject constructor( + private val baseDirectory: File, private val coroutineScope: CoroutineScope, private val coroutineDispatchers: CoroutineDispatchers, - @ApplicationContext context: Context, -) { + private val sessionStore: SessionStore, + private val authService: AuthenticationService, +) : MatrixAuthenticationService { - private val baseDirectory = File(context.filesDir, "sessions") - private val sessionStore = SessionStore(context) - private val authService = AuthenticationService(baseDirectory.absolutePath) - - fun isLoggedIn(): Flow { + override fun isLoggedIn(): Flow { return sessionStore.isLoggedIn() } - suspend fun getLatestSessionId(): SessionId? = withContext(coroutineDispatchers.io) { + override suspend fun getLatestSessionId(): SessionId? = withContext(coroutineDispatchers.io) { sessionStore.getLatestSession()?.sessionId() } - suspend fun restoreSession() = withContext(coroutineDispatchers.io) { + override suspend fun restoreSession() = withContext(coroutineDispatchers.io) { sessionStore.getLatestSession() ?.let { session -> try { @@ -73,17 +71,17 @@ class Matrix @Inject constructor( } } - fun getHomeserver(): String? = authService.homeserverDetails()?.url() + override fun getHomeserver(): String? = authService.homeserverDetails()?.url() - fun getHomeserverOrDefault(): String = getHomeserver() ?: "matrix.org" + override fun getHomeserverOrDefault(): String = getHomeserver() ?: "matrix.org" - suspend fun setHomeserver(homeserver: String) { + override suspend fun setHomeserver(homeserver: String) { withContext(coroutineDispatchers.io) { authService.configureHomeserver(homeserver) } } - suspend fun login(username: String, password: String): SessionId = + override suspend fun login(username: String, password: String): SessionId = withContext(coroutineDispatchers.io) { val client = try { authService.login(username, password, "ElementX Android", null) diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/di/MatrixModule.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/di/MatrixModule.kt new file mode 100644 index 0000000000..2808c3667b --- /dev/null +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/di/MatrixModule.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.matrix.di + +import com.squareup.anvil.annotations.ContributesTo +import dagger.Module +import dagger.Provides +import io.element.android.x.di.AppScope +import io.element.android.x.di.SingleIn +import org.matrix.rustcomponents.sdk.AuthenticationService +import java.io.File + +@Module +@ContributesTo(AppScope::class) +object MatrixModule { + + @Provides + @SingleIn(AppScope::class) + fun providesRustAuthenticationService(baseDirectory: File): AuthenticationService { + return AuthenticationService(baseDirectory.absolutePath) + } +} diff --git a/features/roomlist/src/test/java/io/element/android/x/features/roomlist/ExampleUnitTest.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/media/FakeMediaResolver.kt similarity index 59% rename from features/roomlist/src/test/java/io/element/android/x/features/roomlist/ExampleUnitTest.kt rename to libraries/matrix/src/main/java/io/element/android/x/matrix/media/FakeMediaResolver.kt index 867f8aa958..a84997c511 100644 --- a/features/roomlist/src/test/java/io/element/android/x/features/roomlist/ExampleUnitTest.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/media/FakeMediaResolver.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 2023 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,14 @@ * limitations under the License. */ -package io.element.android.x.features.roomlist +package io.element.android.x.matrix.media -import org.junit.Assert.assertEquals -import org.junit.Test +class FakeMediaResolver : MediaResolver { + override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { + return null + } -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) + override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? { + return null } } diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/room/FakeMatrixRoom.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/FakeMatrixRoom.kt new file mode 100644 index 0000000000..46154bba37 --- /dev/null +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/FakeMatrixRoom.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.matrix.room + +import io.element.android.x.matrix.core.EventId +import io.element.android.x.matrix.core.RoomId +import io.element.android.x.matrix.timeline.FakeMatrixTimeline +import io.element.android.x.matrix.timeline.MatrixTimeline +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +class FakeMatrixRoom( + override val roomId: RoomId, + override val name: String? = null, + override val bestName: String = "", + override val displayName: String = "", + override val topic: String? = null, + override val avatarUrl: String? = null +) : MatrixRoom { + + override fun syncUpdateFlow(): Flow { + return emptyFlow() + } + + override fun timeline(): MatrixTimeline { + return FakeMatrixTimeline() + } + + override suspend fun userDisplayName(userId: String): Result { + return Result.success("") + } + + override suspend fun userAvatarUrl(userId: String): Result { + TODO("Not yet implemented") + } + + override suspend fun sendMessage(message: String): Result { + TODO("Not yet implemented") + } + + override suspend fun editMessage(originalEventId: EventId, message: String): Result { + TODO("Not yet implemented") + } + + override suspend fun replyMessage(eventId: EventId, message: String): Result { + TODO("Not yet implemented") + } + + override suspend fun redactEvent(eventId: EventId, reason: String?): Result { + TODO("Not yet implemented") + } +} diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/room/InMemoryRoomSummaryDataSource.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/InMemoryRoomSummaryDataSource.kt new file mode 100644 index 0000000000..7e2dc952ee --- /dev/null +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/InMemoryRoomSummaryDataSource.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.matrix.room + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +class InMemoryRoomSummaryDataSource : RoomSummaryDataSource { + + override fun roomSummaries(): Flow> { + return emptyFlow() + } + + override fun setSlidingSyncRange(range: IntRange) = Unit +} diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/session/PreferencesSessionStore.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/session/PreferencesSessionStore.kt new file mode 100644 index 0000000000..6c8f6498e3 --- /dev/null +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/session/PreferencesSessionStore.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.matrix.session + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.x.di.AppScope +import io.element.android.x.di.ApplicationContext +import io.element.android.x.di.SingleIn +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.matrix.rustcomponents.sdk.Session +import javax.inject.Inject + +private val Context.dataStore: DataStore by preferencesDataStore(name = "elementx_sessions") + +// TODO It contains the access token, so it has to be stored in a more secured storage. +private val sessionKey = stringPreferencesKey("session") + +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class PreferencesSessionStore @Inject constructor( + @ApplicationContext context: Context +) : SessionStore { + @Serializable + data class SessionData( + val accessToken: String, + val deviceId: String, + val homeserverUrl: String, + val isSoftLogout: Boolean, + val refreshToken: String?, + val userId: String + ) + + private val store = context.dataStore + + override fun isLoggedIn(): Flow { + return store.data.map { prefs -> + prefs[sessionKey] != null + } + } + + override suspend fun storeData(session: Session) { + store.edit { prefs -> + val sessionData = SessionData( + accessToken = session.accessToken, + deviceId = session.deviceId, + homeserverUrl = session.homeserverUrl, + isSoftLogout = session.isSoftLogout, + refreshToken = session.refreshToken, + userId = session.userId + ) + val encodedSession = Json.encodeToString(sessionData) + prefs[sessionKey] = encodedSession + } + } + + override suspend fun getLatestSession(): Session? { + return store.data.firstOrNull()?.let { prefs -> + val encodedSession = prefs[sessionKey] ?: return@let null + val sessionData = Json.decodeFromString(encodedSession) + Session( + accessToken = sessionData.accessToken, + deviceId = sessionData.deviceId, + homeserverUrl = sessionData.homeserverUrl, + isSoftLogout = sessionData.isSoftLogout, + refreshToken = sessionData.refreshToken, + userId = sessionData.userId + ) + } + } + + override suspend fun reset() { + store.edit { it.clear() } + } +} diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/session/SessionStore.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/session/SessionStore.kt index 51550d025d..4380518ce9 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/session/SessionStore.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/session/SessionStore.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 2023 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,78 +16,12 @@ package io.element.android.x.matrix.session -import android.content.Context -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.map -import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json import org.matrix.rustcomponents.sdk.Session -private val Context.dataStore: DataStore by preferencesDataStore(name = "elementx_sessions") - -// TODO It contains the access token, so it has to be stored in a more secured storage. -private val sessionKey = stringPreferencesKey("session") - -internal class SessionStore( - context: Context -) { - @Serializable - data class SessionData( - val accessToken: String, - val deviceId: String, - val homeserverUrl: String, - val isSoftLogout: Boolean, - val refreshToken: String?, - val userId: String - ) - - private val store = context.dataStore - - fun isLoggedIn(): Flow { - return store.data.map { prefs -> - prefs[sessionKey] != null - } - } - - suspend fun storeData(session: Session) { - store.edit { prefs -> - val sessionData = SessionData( - accessToken = session.accessToken, - deviceId = session.deviceId, - homeserverUrl = session.homeserverUrl, - isSoftLogout = session.isSoftLogout, - refreshToken = session.refreshToken, - userId = session.userId - ) - val encodedSession = Json.encodeToString(sessionData) - prefs[sessionKey] = encodedSession - } - } - - suspend fun getLatestSession(): Session? { - return store.data.firstOrNull()?.let { prefs -> - val encodedSession = prefs[sessionKey] ?: return@let null - val sessionData = Json.decodeFromString(encodedSession) - Session( - accessToken = sessionData.accessToken, - deviceId = sessionData.deviceId, - homeserverUrl = sessionData.homeserverUrl, - isSoftLogout = sessionData.isSoftLogout, - refreshToken = sessionData.refreshToken, - userId = sessionData.userId - ) - } - } - - suspend fun reset() { - store.edit { it.clear() } - } +interface SessionStore { + fun isLoggedIn(): Flow + suspend fun storeData(session: Session) + suspend fun getLatestSession(): Session? + suspend fun reset() } diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/FakeMatrixTimeline.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/FakeMatrixTimeline.kt new file mode 100644 index 0000000000..4a7eabbb0f --- /dev/null +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/FakeMatrixTimeline.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.matrix.timeline + +import io.element.android.x.matrix.core.EventId +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import org.matrix.rustcomponents.sdk.TimelineListener + +class FakeMatrixTimeline : MatrixTimeline { + + override var callback: MatrixTimeline.Callback? + get() = TODO("Not yet implemented") + set(value) {} + + override val hasMoreToLoad: Boolean + get() = true + + override fun timelineItems(): Flow> { + return emptyFlow() + } + + override suspend fun paginateBackwards(count: Int): Result { + return Result.success(Unit) + } + + override fun addListener(timelineListener: TimelineListener) { + // + } + + override fun initialize() = Unit + + override fun dispose() = Unit + + override suspend fun sendMessage(message: String): Result { + return Result.success(Unit) + } + + override suspend fun editMessage(originalEventId: EventId, message: String): Result { + return Result.success(Unit) + } + + override suspend fun replyMessage(inReplyToEventId: EventId, message: String): Result { + return Result.success(Unit) + } +} diff --git a/plugins/src/main/java/extension/DependencyHandleScope.kt b/plugins/src/main/java/extension/DependencyHandleScope.kt index bdfa1460bf..05135e4c92 100644 --- a/plugins/src/main/java/extension/DependencyHandleScope.kt +++ b/plugins/src/main/java/extension/DependencyHandleScope.kt @@ -40,9 +40,7 @@ fun DependencyHandlerScope.composeDependencies() { implementation("androidx.compose.material3:material3") implementation("androidx.compose.material:material-icons-extended") implementation("androidx.compose.ui:ui-tooling-preview") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1") implementation("androidx.activity:activity-compose:1.6.1") - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1") debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest") implementation("com.airbnb.android:showkase:1.0.0-beta14") From 1b054aca5bf219f9e82b98ef47dfd0aa36bc370c Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Jan 2023 20:48:29 +0100 Subject: [PATCH 2/3] Clean up after merge --- features/roomlist/build.gradle.kts | 1 - .../x/features/roomlist/ExampleUnitTest.kt | 27 ------------------- 2 files changed, 28 deletions(-) delete mode 100644 features/roomlist/src/test/kotlin/io/element/android/x/features/roomlist/ExampleUnitTest.kt diff --git a/features/roomlist/build.gradle.kts b/features/roomlist/build.gradle.kts index fd86b204bd..69131f1d06 100644 --- a/features/roomlist/build.gradle.kts +++ b/features/roomlist/build.gradle.kts @@ -55,5 +55,4 @@ dependencies { androidTestImplementation(libs.test.junitext) ksp(libs.showkase.processor) - } diff --git a/features/roomlist/src/test/kotlin/io/element/android/x/features/roomlist/ExampleUnitTest.kt b/features/roomlist/src/test/kotlin/io/element/android/x/features/roomlist/ExampleUnitTest.kt deleted file mode 100644 index a84997c511..0000000000 --- a/features/roomlist/src/test/kotlin/io/element/android/x/features/roomlist/ExampleUnitTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2023 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.x.matrix.media - -class FakeMediaResolver : MediaResolver { - override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { - return null - } - - override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? { - return null - } -} From 97efff8aa4fdd5272f62daf05b24b72b4e0f3b4c Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Jan 2023 15:26:06 +0100 Subject: [PATCH 3/3] Changes after review --- features/roomlist/build.gradle.kts | 1 + .../roomlist/RoomListPresenterTests.kt | 7 +++- gradle/libs.versions.toml | 37 ++++++----------- .../android/x/architecture/Bindings.kt | 5 ++- .../android/x/matrix/media/MediaResolver.kt | 22 ---------- .../x/matrix/media/RustMediaResolver.kt | 40 +++++++++++++++++++ libraries/matrixtest/build.gradle.kts | 30 ++++++++++++++ .../matrixtest/src/main/AndroidManifest.xml | 18 +++++++++ .../libraries/matrixtest}/FakeMatrixClient.kt | 9 +++-- .../matrixtest}/media/FakeMediaResolver.kt | 4 +- .../matrixtest}/room/FakeMatrixRoom.kt | 5 ++- .../room/InMemoryRoomSummaryDataSource.kt | 4 +- .../timeline/FakeMatrixTimeline.kt | 10 ++--- settings.gradle.kts | 1 + 14 files changed, 130 insertions(+), 63 deletions(-) create mode 100644 libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/RustMediaResolver.kt create mode 100644 libraries/matrixtest/build.gradle.kts create mode 100644 libraries/matrixtest/src/main/AndroidManifest.xml rename libraries/{matrix/src/main/kotlin/io/element/android/x/matrix => matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest}/FakeMatrixClient.kt (86%) rename libraries/{matrix/src/main/kotlin/io/element/android/x/matrix => matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest}/media/FakeMediaResolver.kt (88%) rename libraries/{matrix/src/main/kotlin/io/element/android/x/matrix => matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest}/room/FakeMatrixRoom.kt (91%) rename libraries/{matrix/src/main/kotlin/io/element/android/x/matrix => matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest}/room/InMemoryRoomSummaryDataSource.kt (84%) rename libraries/{matrix/src/main/kotlin/io/element/android/x/matrix => matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest}/timeline/FakeMatrixTimeline.kt (89%) diff --git a/features/roomlist/build.gradle.kts b/features/roomlist/build.gradle.kts index 69131f1d06..fe2fd050fe 100644 --- a/features/roomlist/build.gradle.kts +++ b/features/roomlist/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(project(":libraries:matrixtest")) androidTestImplementation(libs.test.junitext) diff --git a/features/roomlist/src/test/kotlin/io/element/android/x/features/roomlist/RoomListPresenterTests.kt b/features/roomlist/src/test/kotlin/io/element/android/x/features/roomlist/RoomListPresenterTests.kt index e2e85e8db3..cd1b148ce5 100644 --- a/features/roomlist/src/test/kotlin/io/element/android/x/features/roomlist/RoomListPresenterTests.kt +++ b/features/roomlist/src/test/kotlin/io/element/android/x/features/roomlist/RoomListPresenterTests.kt @@ -20,7 +20,7 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.x.matrix.FakeMatrixClient +import io.element.android.x.libraries.matrixtest.FakeMatrixClient import io.element.android.x.matrix.core.SessionId import kotlinx.coroutines.test.runTest import org.junit.Test @@ -30,7 +30,10 @@ class RoomListPresenterTests { @Test fun `present - should start with no user and then load user with success`() = runTest { - val presenter = RoomListPresenter(FakeMatrixClient(SessionId("sessionId")), LastMessageFormatter()) + val presenter = RoomListPresenter( + FakeMatrixClient( + SessionId("sessionId") + ), LastMessageFormatter()) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 26963a3f0f..9f7df7ddb0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,26 +29,15 @@ coroutines = "1.6.4" accompanist = "0.27.0" # Test -test_junit = "4.13.2" -test_runner = "1.4.0" test_core = "1.4.0" -test_mockk = "1.13.2" -test_uiautomator = "2.2.0" -test_junitext = "1.1.3" -test_barista = "4.2.0" -test_hamcrest = "2.2" -test_orchestrator = "1.4.1" -test_turbine = "0.12.1" #other coil = "2.2.2" datetime = "0.4.0" -wysiwyg = "0.7.0.1" serialization_json = "1.4.1" showkase = "1.0.0-beta14" jsoup = "1.15.3" appyx = "1.0.1" -seismic = "1.0.3" dependencycheck = "7.4.4" stem = "2.2.3" @@ -98,20 +87,20 @@ accompanist_pagerindicator = { module = "com.google.accompanist:accompanist-page accompanist_flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" } # Libraries -squareup_seismic = { module = "com.squareup:seismic", version.ref = "seismic" } +squareup_seismic = "com.squareup:seismic:1.0.3" # Test -test_junit = { module = "junit:junit", version.ref = "test_junit" } -test_runner = { module = "androidx.test:runner", version.ref = "test_runner" } test_core = { module = "androidx.test:core", version.ref = "test_core" } test_corektx = { module = "androidx.test:core-ktx", version.ref = "test_core" } -test_uiautomator = { module = "androidx.test.uiautomator:uiautomator", version.ref = "test_uiautomator" } -test_junitext = { module = "androidx.test.ext:junit", version.ref = "test_junitext" } -test_mockk = { module = "io.mockk:mockk", version.ref = "test_mockk" } -test_barista = { module = "com.adevinta.android:barista", version.ref = "test_barista" } -test_hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "test_hamcrest" } -test_orchestrator = { module = "androidx.test:orchestrator", version.ref = "test_orchestrator" } -test_turbine = { module = "app.cash.turbine:turbine", version.ref = "test_turbine"} +test_junit = "junit:junit:4.13.2" +test_runner = "androidx.test:runner:1.4.0" +test_uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0" +test_junitext = "androidx.test.ext:junit:1.1.3" +test_mockk = "io.mockk:mockk:1.13.2" +test_barista = "com.adevinta.android:barista:4.2.0" +test_hamcrest = "org.hamcrest:hamcrest:2.2" +test_orchestrator = "androidx.test:orchestrator:1.4.1" +test_turbine = "app.cash.turbine:turbine:0.12.1" test_truth = "com.google.truth:truth:1.1.3" @@ -123,18 +112,18 @@ serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-jso showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } -appyx_core = {module = "com.bumble.appyx:core", version.ref = "appyx"} +appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } # Di -inject = { module = "javax.inject:javax.inject", version = "1" } +inject = "javax.inject:javax.inject:1" dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" } dagger_compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" } anvil_compiler_api = { module = "com.squareup.anvil:compiler-api", version.ref = "anvil" } anvil_compiler_utils = { module = "com.squareup.anvil:compiler-utils", version.ref = "anvil" } # Composer -wysiwyg = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } +wysiwyg = "io.element.android:wysiwyg:0.7.1" [bundles] diff --git a/libraries/architecture/src/main/kotlin/io/element/android/x/architecture/Bindings.kt b/libraries/architecture/src/main/kotlin/io/element/android/x/architecture/Bindings.kt index fadb0764dd..be09103db3 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/x/architecture/Bindings.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/x/architecture/Bindings.kt @@ -19,6 +19,7 @@ package io.element.android.x.architecture import android.content.Context import android.content.ContextWrapper import com.bumble.appyx.core.node.Node +import io.element.android.x.di.DaggerComponentOwner inline fun Node.bindings() = bindings(T::class.java) inline fun Context.bindings() = bindings(T::class.java) @@ -27,7 +28,7 @@ fun Context.bindings(klass: Class): T { // search dagger components in the context hierarchy return generateSequence(this) { (it as? ContextWrapper)?.baseContext } .plus(applicationContext) - .filterIsInstance() + .filterIsInstance() .map { it.daggerComponent } .flatMap { if (it is Collection<*>) it else listOf(it) } .filterIsInstance(klass) @@ -38,7 +39,7 @@ fun Context.bindings(klass: Class): T { fun Node.bindings(klass: Class): T { // search dagger components in node hierarchy return generateSequence(this, Node::parent) - .filterIsInstance() + .filterIsInstance() .map { it.daggerComponent } .flatMap { if (it is Collection<*>) it else listOf(it) } .filterIsInstance(klass) diff --git a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/MediaResolver.kt b/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/MediaResolver.kt index e89c7fa390..0fb796a421 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/MediaResolver.kt +++ b/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/MediaResolver.kt @@ -16,9 +16,7 @@ package io.element.android.x.matrix.media -import io.element.android.x.matrix.MatrixClient import org.matrix.rustcomponents.sdk.MediaSource -import org.matrix.rustcomponents.sdk.mediaSourceFromUrl interface MediaResolver { @@ -39,23 +37,3 @@ interface MediaResolver { suspend fun resolve(meta: Meta): ByteArray? } - -internal class RustMediaResolver(private val client: MatrixClient) : MediaResolver { - - override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { - if (url.isNullOrEmpty()) return null - val mediaSource = mediaSourceFromUrl(url) - return resolve(MediaResolver.Meta(mediaSource, kind)) - } - - override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? { - return when (meta.kind) { - is MediaResolver.Kind.Content -> client.loadMediaContentForSource(meta.source) - is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnailForSource( - meta.source, - meta.kind.width.toLong(), - meta.kind.height.toLong() - ) - }.getOrNull() - } -} diff --git a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/RustMediaResolver.kt b/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/RustMediaResolver.kt new file mode 100644 index 0000000000..f8c1ca3868 --- /dev/null +++ b/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/RustMediaResolver.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.x.matrix.media + +import io.element.android.x.matrix.MatrixClient +import org.matrix.rustcomponents.sdk.mediaSourceFromUrl + +internal class RustMediaResolver(private val client: MatrixClient) : MediaResolver { + + override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { + if (url.isNullOrEmpty()) return null + val mediaSource = mediaSourceFromUrl(url) + return resolve(MediaResolver.Meta(mediaSource, kind)) + } + + override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? { + return when (meta.kind) { + is MediaResolver.Kind.Content -> client.loadMediaContentForSource(meta.source) + is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnailForSource( + meta.source, + meta.kind.width.toLong(), + meta.kind.height.toLong() + ) + }.getOrNull() + } +} diff --git a/libraries/matrixtest/build.gradle.kts b/libraries/matrixtest/build.gradle.kts new file mode 100644 index 0000000000..f4ba43aaac --- /dev/null +++ b/libraries/matrixtest/build.gradle.kts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed +@Suppress("DSL_SCOPE_VIOLATION") +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.x.libraries.matrix.test" +} + +dependencies { + api(project(":libraries:matrix")) + api(libs.coroutines.core) +} diff --git a/libraries/matrixtest/src/main/AndroidManifest.xml b/libraries/matrixtest/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..6a35d06cce --- /dev/null +++ b/libraries/matrixtest/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + diff --git a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/FakeMatrixClient.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/FakeMatrixClient.kt similarity index 86% rename from libraries/matrix/src/main/kotlin/io/element/android/x/matrix/FakeMatrixClient.kt rename to libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/FakeMatrixClient.kt index f90540da83..5e55b6dc79 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/FakeMatrixClient.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/FakeMatrixClient.kt @@ -14,15 +14,16 @@ * limitations under the License. */ -package io.element.android.x.matrix +package io.element.android.x.libraries.matrixtest +import io.element.android.x.matrix.MatrixClient import io.element.android.x.matrix.core.RoomId import io.element.android.x.matrix.core.SessionId import io.element.android.x.matrix.core.UserId -import io.element.android.x.matrix.media.FakeMediaResolver +import io.element.android.x.libraries.matrixtest.media.FakeMediaResolver import io.element.android.x.matrix.media.MediaResolver -import io.element.android.x.matrix.room.FakeMatrixRoom -import io.element.android.x.matrix.room.InMemoryRoomSummaryDataSource +import io.element.android.x.libraries.matrixtest.room.FakeMatrixRoom +import io.element.android.x.libraries.matrixtest.room.InMemoryRoomSummaryDataSource import io.element.android.x.matrix.room.MatrixRoom import io.element.android.x.matrix.room.RoomSummaryDataSource import org.matrix.rustcomponents.sdk.MediaSource diff --git a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/FakeMediaResolver.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/media/FakeMediaResolver.kt similarity index 88% rename from libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/FakeMediaResolver.kt rename to libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/media/FakeMediaResolver.kt index a84997c511..07fc24c468 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/media/FakeMediaResolver.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/media/FakeMediaResolver.kt @@ -14,7 +14,9 @@ * limitations under the License. */ -package io.element.android.x.matrix.media +package io.element.android.x.libraries.matrixtest.media + +import io.element.android.x.matrix.media.MediaResolver class FakeMediaResolver : MediaResolver { override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { diff --git a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/room/FakeMatrixRoom.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/room/FakeMatrixRoom.kt similarity index 91% rename from libraries/matrix/src/main/kotlin/io/element/android/x/matrix/room/FakeMatrixRoom.kt rename to libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/room/FakeMatrixRoom.kt index 46154bba37..fca74a59d9 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/room/FakeMatrixRoom.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/room/FakeMatrixRoom.kt @@ -14,11 +14,12 @@ * limitations under the License. */ -package io.element.android.x.matrix.room +package io.element.android.x.libraries.matrixtest.room import io.element.android.x.matrix.core.EventId import io.element.android.x.matrix.core.RoomId -import io.element.android.x.matrix.timeline.FakeMatrixTimeline +import io.element.android.x.matrix.room.MatrixRoom +import io.element.android.x.libraries.matrixtest.timeline.FakeMatrixTimeline import io.element.android.x.matrix.timeline.MatrixTimeline import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow diff --git a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/room/InMemoryRoomSummaryDataSource.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/room/InMemoryRoomSummaryDataSource.kt similarity index 84% rename from libraries/matrix/src/main/kotlin/io/element/android/x/matrix/room/InMemoryRoomSummaryDataSource.kt rename to libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/room/InMemoryRoomSummaryDataSource.kt index 7e2dc952ee..eaa75e1448 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/room/InMemoryRoomSummaryDataSource.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/room/InMemoryRoomSummaryDataSource.kt @@ -14,8 +14,10 @@ * limitations under the License. */ -package io.element.android.x.matrix.room +package io.element.android.x.libraries.matrixtest.room +import io.element.android.x.matrix.room.RoomSummary +import io.element.android.x.matrix.room.RoomSummaryDataSource import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow diff --git a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/timeline/FakeMatrixTimeline.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/timeline/FakeMatrixTimeline.kt similarity index 89% rename from libraries/matrix/src/main/kotlin/io/element/android/x/matrix/timeline/FakeMatrixTimeline.kt rename to libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/timeline/FakeMatrixTimeline.kt index 4a7eabbb0f..27443bde6f 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/x/matrix/timeline/FakeMatrixTimeline.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/x/libraries/matrixtest/timeline/FakeMatrixTimeline.kt @@ -14,9 +14,11 @@ * limitations under the License. */ -package io.element.android.x.matrix.timeline +package io.element.android.x.libraries.matrixtest.timeline import io.element.android.x.matrix.core.EventId +import io.element.android.x.matrix.timeline.MatrixTimeline +import io.element.android.x.matrix.timeline.MatrixTimelineItem import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import org.matrix.rustcomponents.sdk.TimelineListener @@ -24,7 +26,7 @@ import org.matrix.rustcomponents.sdk.TimelineListener class FakeMatrixTimeline : MatrixTimeline { override var callback: MatrixTimeline.Callback? - get() = TODO("Not yet implemented") + get() = null set(value) {} override val hasMoreToLoad: Boolean @@ -38,9 +40,7 @@ class FakeMatrixTimeline : MatrixTimeline { return Result.success(Unit) } - override fun addListener(timelineListener: TimelineListener) { - // - } + override fun addListener(timelineListener: TimelineListener) = Unit override fun initialize() = Unit diff --git a/settings.gradle.kts b/settings.gradle.kts index 2e1ed9af84..43e3ebb8c1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -53,3 +53,4 @@ include(":libraries:di") include(":anvilannotations") include(":anvilcodegen") include(":libraries:architecture") +include(":libraries:matrixtest")