diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e9468fe83..9d926e4fb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,7 @@ name: APK Build on: + workflow_dispatch: pull_request: { } push: branches: [ main, develop ] @@ -37,11 +38,13 @@ jobs: app/build/outputs/apk/debug/*.apk - uses: rnkdsh/action-upload-diawi@v1.3.1 id: diawi + if: ${{ github.event_name == 'pull_request' }} with: token: ${{ secrets.DIAWI_TOKEN }} file: app/build/outputs/apk/debug/app-arm64-v8a-debug.apk - name: Add or update PR comment with QR Code to download APK. - uses: NejcZdovc/comment-pr@v1 + if: ${{ github.event_name == 'pull_request' }} + uses: NejcZdovc/comment-pr@v2 with: message: | :iphone: Scan the QR code below to install the build (arm64 only) for this PR. diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index a88a5faa9d..4c00894a36 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -11,7 +11,7 @@ jobs: - run: | npm install --save-dev @babel/plugin-transform-flow-strip-types - name: Danger - uses: danger/danger-js@11.2.3 + uses: danger/danger-js@11.2.4 with: args: "--dangerfile ./tools/danger/dangerfile.js" env: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 31e41a28a3..09b66d267b 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -38,7 +38,7 @@ jobs: yarn add danger-plugin-lint-report --dev - name: Danger lint if: always() - uses: danger/danger-js@11.2.3 + uses: danger/danger-js@11.2.4 with: args: "--dangerfile ./tools/danger/dangerfile-lint.js" env: diff --git a/.maestro/tests/account/changeServer.yaml b/.maestro/tests/account/changeServer.yaml index 3f360e70a1..505a12d2e1 100644 --- a/.maestro/tests/account/changeServer.yaml +++ b/.maestro/tests/account/changeServer.yaml @@ -1,5 +1,6 @@ appId: ${APP_ID} --- -- tapOn: "Change" +- tapOn: + id: "login-change_server" - takeScreenshot: build/maestro/200-ChangeServer - tapOn: "Continue" diff --git a/.maestro/tests/account/login.yaml b/.maestro/tests/account/login.yaml index 1baf07be5b..432969425f 100644 --- a/.maestro/tests/account/login.yaml +++ b/.maestro/tests/account/login.yaml @@ -5,17 +5,14 @@ appId: ${APP_ID} - takeScreenshot: build/maestro/100-SignIn - runFlow: changeServer.yaml - runFlow: ../assertions/assertLoginDisplayed.yaml -- tapOn: "Username or email" -# ios -# - tapOn: -# id: "usernameTextField" -# index: 0 +- tapOn: + id: "login-email_username" - inputText: ${USERNAME} +- pressKey: Enter - tapOn: "Password" -# iOS -#- tapOn: -# id: "passwordTextField" -# index: 0 +- tapOn: + id: "login-password" - inputText: ${PASSWORD} +- pressKey: Enter - tapOn: "Continue" - runFlow: ../assertions/assertHomeDisplayed.yaml diff --git a/README.md b/README.md index 593c76cdc6..62a33091e3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ElementX Android is a [Matrix](https://matrix.org/) Android Client provided by [Element](https://element.io/). -The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 5+. The UI layer is written using Jetpack compose. +The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using Jetpack compose. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d0716a23d7..b667b5d0a4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -198,6 +198,9 @@ knit { dependencies { allLibraries() allFeatures() + implementation(projects.libraries.matrix.impl) + implementation(projects.libraries.dateformatter.impl) + implementation(projects.libraries.sessionStorage.impl) implementation(projects.tests.uitests) implementation(projects.anvilannotations) implementation(projects.appnav) @@ -221,5 +224,5 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) + testImplementation(projects.libraries.matrix.test) } diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index fb682318bb..742b42ba63 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -18,8 +18,10 @@ package io.element.android.x import android.os.Bundle import androidx.activity.compose.setContent +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme import androidx.compose.ui.Modifier import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat @@ -40,7 +42,7 @@ class MainActivity : NodeComponentActivity() { setContent { ElementTheme { Box( - modifier = Modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background), ) { NodeHost(integrationPoint = appyxIntegrationPoint) { MainNode(it, appBindings.mainDaggerComponentOwner()) diff --git a/app/src/main/kotlin/io/element/android/x/MainNode.kt b/app/src/main/kotlin/io/element/android/x/MainNode.kt index 23c0c05c37..6b7dee92b8 100644 --- a/app/src/main/kotlin/io/element/android/x/MainNode.kt +++ b/app/src/main/kotlin/io/element/android/x/MainNode.kt @@ -30,8 +30,8 @@ import io.element.android.appnav.RootFlowNode import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.DaggerComponentOwner -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.room.MatrixRoom +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.x.di.MainDaggerComponentsOwner import io.element.android.x.di.RoomComponent import io.element.android.x.di.SessionComponent diff --git a/app/src/main/kotlin/io/element/android/x/di/RoomComponent.kt b/app/src/main/kotlin/io/element/android/x/di/RoomComponent.kt index a2c2ce3881..68c700bdb8 100644 --- a/app/src/main/kotlin/io/element/android/x/di/RoomComponent.kt +++ b/app/src/main/kotlin/io/element/android/x/di/RoomComponent.kt @@ -24,7 +24,7 @@ import io.element.android.libraries.architecture.NodeFactoriesBindings import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SingleIn -import io.element.android.libraries.matrix.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.MatrixRoom @SingleIn(RoomScope::class) @MergeSubcomponent(RoomScope::class) diff --git a/app/src/main/kotlin/io/element/android/x/di/SessionComponent.kt b/app/src/main/kotlin/io/element/android/x/di/SessionComponent.kt index 075529e3e8..54e8c27498 100644 --- a/app/src/main/kotlin/io/element/android/x/di/SessionComponent.kt +++ b/app/src/main/kotlin/io/element/android/x/di/SessionComponent.kt @@ -24,7 +24,7 @@ import io.element.android.libraries.architecture.NodeFactoriesBindings import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SingleIn -import io.element.android.libraries.matrix.MatrixClient +import io.element.android.libraries.matrix.api.MatrixClient @SingleIn(SessionScope::class) @MergeSubcomponent(SessionScope::class) diff --git a/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt b/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt index 9af7738fa8..5eebc88756 100644 --- a/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt +++ b/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt @@ -18,8 +18,8 @@ package io.element.android.x.initializer import android.content.Context import androidx.startup.Initializer -import io.element.android.libraries.matrix.tracing.TracingConfigurations -import io.element.android.libraries.matrix.tracing.setupTracing +import io.element.android.libraries.matrix.impl.tracing.setupTracing +import io.element.android.libraries.matrix.api.tracing.TracingConfigurations import io.element.android.x.BuildConfig class MatrixInitializer : Initializer { diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index cb8d2d3595..4b36c75055 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -28,7 +28,6 @@ plugins { android { namespace = "io.element.android.appnav" - } dependencies { @@ -47,7 +46,7 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.matrixui) implementation(projects.tests.uitests) @@ -58,5 +57,5 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) + testImplementation(projects.libraries.matrix.test) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index ce70f4ba03..d072870de3 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -46,8 +46,8 @@ import io.element.android.libraries.architecture.nodeInputs import io.element.android.libraries.architecture.nodeInputsProvider import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.di.AppScope -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.di.MatrixUIBindings import kotlinx.parcelize.Parcelize diff --git a/appnav/src/main/kotlin/io/element/android/appnav/MatrixClientsHolder.kt b/appnav/src/main/kotlin/io/element/android/appnav/MatrixClientsHolder.kt index a0f2d38d52..0a96ad2c74 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/MatrixClientsHolder.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/MatrixClientsHolder.kt @@ -19,9 +19,10 @@ package io.element.android.appnav import android.os.Bundle import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.auth.MatrixAuthenticationService -import io.element.android.libraries.matrix.core.SessionId +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId import kotlinx.coroutines.runBlocking import timber.log.Timber import java.util.concurrent.ConcurrentHashMap @@ -57,13 +58,13 @@ class MatrixClientsHolder @Inject constructor(private val authenticationService: @Suppress("DEPRECATION") fun restore(savedInstanceState: Bundle?) { if (savedInstanceState == null || sessionIdsToMatrixClient.isNotEmpty()) return - val sessionIds = savedInstanceState.getSerializable(SAVE_INSTANCE_KEY) as? Array - if (sessionIds.isNullOrEmpty()) return + val userIds = savedInstanceState.getSerializable(SAVE_INSTANCE_KEY) as? Array + if (userIds.isNullOrEmpty()) return // Not ideal but should only happens in case of process recreation. This ensure we restore all the active sessions before restoring the node graphs. runBlocking { - sessionIds.forEach { sessionId -> - Timber.v("Restore matrix session: $sessionId") - val matrixClient = authenticationService.restoreSession(sessionId) + userIds.forEach { userId -> + Timber.v("Restore matrix session: $userId") + val matrixClient = authenticationService.restoreSession(userId) if (matrixClient != null) { add(matrixClient) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RoomFlowNode.kt index 0ef806208c..7025617719 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RoomFlowNode.kt @@ -34,7 +34,7 @@ import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.nodeInputs import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.MatrixRoom import kotlinx.parcelize.Parcelize import timber.log.Timber diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt index 817e119035..3baa775620 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt @@ -49,8 +49,9 @@ import io.element.android.libraries.architecture.nodeInputsProvider import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext -import io.element.android.libraries.matrix.auth.MatrixAuthenticationService -import io.element.android.libraries.matrix.core.SessionId +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.tests.uitests.openShowkase import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn @@ -100,7 +101,7 @@ class RootFlowNode @AssistedInject constructor( } private fun switchToLoggedInFlow(sessionId: SessionId) { - backstack.safeRoot(NavTarget.LoggedInFlow(sessionId = sessionId)) + backstack.safeRoot(NavTarget.LoggedInFlow(sessionId)) } private fun switchToLogoutFlow() { @@ -109,19 +110,19 @@ class RootFlowNode @AssistedInject constructor( } private suspend fun tryToRestoreLatestSession( - onSuccess: (SessionId) -> Unit = {}, + onSuccess: (UserId) -> Unit = {}, onFailure: () -> Unit = {} ) { - val latestKnownSessionId = authenticationService.getLatestSessionId() - if (latestKnownSessionId == null) { + val latestKnownUserId = authenticationService.getLatestSessionId() + if (latestKnownUserId == null) { onFailure() return } - if (matrixClientsHolder.knowSession(latestKnownSessionId)) { - onSuccess(latestKnownSessionId) + if (matrixClientsHolder.knowSession(latestKnownUserId)) { + onSuccess(latestKnownUserId) return } - val matrixClient = authenticationService.restoreSession(latestKnownSessionId) + val matrixClient = authenticationService.restoreSession(UserId(latestKnownUserId.value)) if (matrixClient == null) { Timber.v("Failed to restore session...") onFailure() @@ -141,6 +142,7 @@ class RootFlowNode @AssistedInject constructor( fun openShowkase() { openShowkase(activity) } + val state = presenter.present() RootView( state = state, diff --git a/appnav/src/test/kotlin/io/element/android/appnav/FakeBugReporter.kt b/appnav/src/test/kotlin/io/element/android/appnav/FakeBugReporter.kt index 3f17841274..d0caa6d838 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/FakeBugReporter.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/FakeBugReporter.kt @@ -19,7 +19,7 @@ package io.element.android.appnav import io.element.android.features.rageshake.reporter.BugReporter import io.element.android.features.rageshake.reporter.BugReporterListener import io.element.android.features.rageshake.reporter.ReportType -import io.element.android.libraries.matrixtest.A_FAILURE_REASON +import io.element.android.libraries.matrix.test.A_FAILURE_REASON import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch diff --git a/build.gradle.kts b/build.gradle.kts index b647d04ff1..2f7ebd91d2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -60,7 +60,7 @@ allprojects { config = files("$rootDir/tools/detekt/detekt.yml") } dependencies { - detektPlugins("com.twitter.compose.rules:detekt:0.0.26") + detektPlugins("io.nlopez.compose.rules:detekt:0.1.2") } // KtLint @@ -214,11 +214,11 @@ koverMerged { name = "Global minimum code coverage." target = kotlinx.kover.api.VerificationTarget.ALL bound { - minValue = 50 + minValue = 55 // Setting a max value, so that if coverage is bigger, it means that we have to change minValue. // For instance if we have minValue = 25 and maxValue = 30, and current code coverage is now 37.32%, update // minValue to 35 and maxValue to 40. - maxValue = 55 + maxValue = 60 counter = kotlinx.kover.api.CounterType.INSTRUCTION valueType = kotlinx.kover.api.VerificationValueType.COVERED_PERCENTAGE } @@ -303,3 +303,15 @@ tasks.register("runQualityChecks") { } dependsOn(":app:knitCheck") } + +// Make sure to delete old screenshots before recording new ones +subprojects { + val snapshotsDir = File("${project.path}/src/test/snapshots") + val removeOldScreenshotsTask = tasks.register("removeOldSnapshots") { + onlyIf { snapshotsDir.exists() } + doFirst { + snapshotsDir.deleteRecursively() + } + } + tasks.findByName("recordPaparazzi")?.dependsOn(removeOldScreenshotsTask) +} diff --git a/changelog.d/138.bugfix b/changelog.d/138.bugfix new file mode 100644 index 0000000000..f33fbe310d --- /dev/null +++ b/changelog.d/138.bugfix @@ -0,0 +1,3 @@ +Add consumer proguard rules for SQLCipher. + +Thanks @anoadragon453 for reporting and the tentative fix! diff --git a/changelog.d/84.feature b/changelog.d/84.feature new file mode 100644 index 0000000000..7cbcaa26cc --- /dev/null +++ b/changelog.d/84.feature @@ -0,0 +1 @@ +Store session data in a secure storage. diff --git a/changelog.d/88.bugfix b/changelog.d/88.bugfix new file mode 100644 index 0000000000..8aa2956907 --- /dev/null +++ b/changelog.d/88.bugfix @@ -0,0 +1 @@ +Fix designs in sign in and change server flows diff --git a/features/login/build.gradle.kts b/features/login/build.gradle.kts index 29ddbec57d..4b5504ec34 100644 --- a/features/login/build.gradle.kts +++ b/features/login/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { anvil(projects.anvilcodegen) implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.elementresources) implementation(projects.libraries.testtags) @@ -48,7 +48,7 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) + testImplementation(projects.libraries.matrix.test) androidTestImplementation(libs.test.junitext) } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvents.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvents.kt index d510b25f20..dd1e83c3f8 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvents.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvents.kt @@ -19,4 +19,5 @@ package io.element.android.features.login.impl.changeserver sealed interface ChangeServerEvents { data class SetServer(val server: String) : ChangeServerEvents object Submit : ChangeServerEvents + object ClearError : ChangeServerEvents } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerNode.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerNode.kt index 9d000a5a44..39a4732b6d 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerNode.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerNode.kt @@ -16,14 +16,20 @@ package io.element.android.features.login.impl.changeserver +import android.content.Context +import android.content.Intent +import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.login.util.LoginConstants +import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.di.AppScope @ContributesNode(AppScope::class) @@ -32,18 +38,25 @@ class ChangeServerNode @AssistedInject constructor( @Assisted plugins: List, private val presenter: ChangeServerPresenter, ) : Node(buildContext, plugins = plugins) { - private fun onSuccess() { navigateUp() } + private fun openLearnMorePage(context: Context) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(LoginConstants.SLIDING_SYNC_READ_MORE_URL)) + tryOrNull { context.startActivity(intent) } + } + @Composable override fun View(modifier: Modifier) { val state = presenter.present() + val context = LocalContext.current ChangeServerView( state = state, modifier = modifier, onChangeServerSuccess = this::onSuccess, + onBackPressed = { navigateUp() }, + onLearnMoreClicked = { openLearnMorePage(context) }, ) } } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index b4c872853e..e97a47387a 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -22,12 +22,15 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable +import io.element.android.features.login.util.LoginConstants import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.execute -import io.element.android.libraries.matrix.auth.MatrixAuthenticationService +import io.element.android.libraries.core.data.tryOrNull +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import java.net.URL import javax.inject.Inject class ChangeServerPresenter @Inject constructor(private val authenticationService: MatrixAuthenticationService) : Presenter { @@ -35,8 +38,9 @@ class ChangeServerPresenter @Inject constructor(private val authenticationServic @Composable override fun present(): ChangeServerState { val localCoroutineScope = rememberCoroutineScope() + val homeserver = rememberSaveable { - mutableStateOf(authenticationService.getHomeserverOrDefault()) + mutableStateOf(authenticationService.getHomeserverDetails().value?.url ?: LoginConstants.DEFAULT_HOMESERVER_URL) } val changeServerAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) @@ -45,7 +49,10 @@ class ChangeServerPresenter @Inject constructor(private val authenticationServic fun handleEvents(event: ChangeServerEvents) { when (event) { is ChangeServerEvents.SetServer -> homeserver.value = event.server - ChangeServerEvents.Submit -> localCoroutineScope.submit(homeserver.value, changeServerAction) + ChangeServerEvents.Submit -> { + localCoroutineScope.submit(homeserver, changeServerAction) + } + ChangeServerEvents.ClearError -> changeServerAction.value = Async.Uninitialized } } @@ -56,9 +63,11 @@ class ChangeServerPresenter @Inject constructor(private val authenticationServic ) } - private fun CoroutineScope.submit(homeserver: String, changeServerAction: MutableState>) = launch { + private fun CoroutineScope.submit(homeserverUrl: MutableState, changeServerAction: MutableState>) = launch { suspend { - authenticationService.setHomeserver(homeserver) + val domain = tryOrNull { URL(homeserverUrl.value) }?.host ?: homeserverUrl.value + authenticationService.setHomeserver(domain) + homeserverUrl.value = domain }.execute(changeServerAction) } } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index ee72761cff..f8e136f011 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -19,164 +19,240 @@ package io.element.android.features.login.impl.changeserver import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import io.element.android.features.login.R -import io.element.android.features.login.impl.error.changeServerError import io.element.android.libraries.architecture.Async +import io.element.android.libraries.designsystem.ElementTextStyles +import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.LocalColors +import io.element.android.libraries.designsystem.theme.components.BackButton import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.OutlinedTextField +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextField +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag +import org.matrix.rustcomponents.sdk.AuthenticationException +import io.element.android.libraries.ui.strings.R as StringR +@OptIn(ExperimentalMaterial3Api::class) @Composable fun ChangeServerView( state: ChangeServerState, + onLearnMoreClicked: () -> Unit, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, onChangeServerSuccess: () -> Unit = {}, ) { val eventSink = state.eventSink val scrollState = rememberScrollState() - Box( - modifier = modifier - .fillMaxSize() - .systemBarsPadding() - .imePadding() - ) { - Column( - modifier = Modifier - .verticalScroll( - state = scrollState, - ) - .padding(horizontal = 16.dp) - ) { - val isError = state.changeServerAction is Async.Failure - Box( - modifier = Modifier - .padding(top = 99.dp) - .size(width = 81.dp, height = 73.dp) - .align(Alignment.CenterHorizontally) - .background( - color = MaterialTheme.colorScheme.surfaceVariant, - shape = RoundedCornerShape(32.dp) - ) - ) { - Icon( - modifier = Modifier - .align(Alignment.Center) - .size(width = 48.dp, height = 48.dp), - // TODO Update with design input - resourceId = R.drawable.ic_baseline_dataset_24, - contentDescription = "", - ) - } - Text( - text = "Your server", - modifier = Modifier - .fillMaxWidth() - .defaultMinSize(minHeight = 56.dp) - .align(Alignment.CenterHorizontally) - .padding(top = 38.dp), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - fontSize = 24.sp, - color = MaterialTheme.colorScheme.primary, - ) - Text( - text = "A server is a home for all your data.\n" + - "You choose your server and it’s easy to make one.", // TODO "Learn more.", - modifier = Modifier - .fillMaxWidth() - .align(Alignment.CenterHorizontally) - .padding(top = 16.dp), - textAlign = TextAlign.Center, - fontSize = 16.sp, - color = MaterialTheme.colorScheme.secondary, - ) - var homeserverFieldState by textFieldState(stateValue = state.homeserver) - OutlinedTextField( - value = homeserverFieldState, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.changeServerServer) - .padding(top = 200.dp), - onValueChange = { - homeserverFieldState = it - eventSink(ChangeServerEvents.SetServer(it)) - }, - label = { - Text(text = "Server") - }, - isError = isError, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Password, - imeAction = ImeAction.Done, - ), - keyboardActions = KeyboardActions( - onDone = { eventSink(ChangeServerEvents.Submit) } - ) - ) - if (state.changeServerAction is Async.Failure) { - Text( - text = changeServerError( - state.homeserver, - state.changeServerAction.error - ), - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp) - ) - } - Button( - onClick = { eventSink(ChangeServerEvents.Submit) }, - enabled = state.submitEnabled, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.changeServerContinue) - .padding(top = 44.dp) - ) { - Text(text = "Continue") - } - if (state.changeServerAction is Async.Success) { - onChangeServerSuccess() - } - } - if (state.changeServerAction is Async.Loading) { - CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) - ) + val interactionEnabled by remember(state.changeServerAction) { + derivedStateOf { + state.changeServerAction !is Async.Loading } } + val focusManager = LocalFocusManager.current + Scaffold( + topBar = { + TopAppBar( + title = {}, + navigationIcon = { BackButton(action = onBackPressed, enabled = interactionEnabled) } + ) + } + ) { padding -> + Box( + modifier = modifier + .fillMaxSize() + .imePadding() + .padding(padding) + ) { + Column( + modifier = Modifier + .verticalScroll( + state = scrollState, + ) + .padding(horizontal = 16.dp) + ) { + Spacer(Modifier.height(42.dp)) + Box( + modifier = Modifier + .size(width = 70.dp, height = 70.dp) + .align(Alignment.CenterHorizontally) + .background( + color = LocalColors.current.quinary, + shape = RoundedCornerShape(14.dp) + ) + ) { + Icon( + modifier = Modifier + .align(Alignment.Center) + .size(width = 32.dp, height = 32.dp), + tint = MaterialTheme.colorScheme.secondary, + resourceId = R.drawable.ic_homeserver, + contentDescription = "", + ) + } + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = StringR.string.ftue_auth_choose_server_title), + modifier = Modifier + .fillMaxWidth() + .align(Alignment.CenterHorizontally), + textAlign = TextAlign.Center, + style = ElementTextStyles.Bold.title2, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(Modifier.height(8.dp)) + Text( + text = stringResource(id = StringR.string.ex_choose_server_subtitle), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + textAlign = TextAlign.Center, + style = ElementTextStyles.Regular.subheadline, + color = MaterialTheme.colorScheme.secondary, + ) + Spacer(Modifier.height(24.dp)) + Text( + stringResource(StringR.string.hs_url), + style = ElementTextStyles.Regular.formHeader, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) + ) + var homeserverFieldState by textFieldState(stateValue = state.homeserver) + TextField( + value = homeserverFieldState, + readOnly = !interactionEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.changeServerServer) + .onTabOrEnterKeyFocusNext(focusManager), + onValueChange = { + homeserverFieldState = it + eventSink(ChangeServerEvents.SetServer(it)) + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions( + onDone = { eventSink(ChangeServerEvents.Submit) } + ), + singleLine = true, + maxLines = 1, + trailingIcon = if (homeserverFieldState.isNotEmpty()) { + { + IconButton(onClick = { + homeserverFieldState = "" + }, enabled = interactionEnabled) { + Icon(imageVector = Icons.Filled.Close, contentDescription = stringResource(StringR.string.action_clear)) + } + } + } else null, + ) + if (state.changeServerAction is Async.Failure) { + if (state.changeServerAction.error is AuthenticationException.SlidingSyncNotAvailable) { + SlidingSyncNotSupportedDialog(onLearnMoreClicked = { + onLearnMoreClicked() + eventSink(ChangeServerEvents.ClearError) + }, onDismiss = { + eventSink(ChangeServerEvents.ClearError) + }) + } else { + ChangeServerErrorDialog( + error = state.changeServerAction.error, + onDismiss = { + eventSink(ChangeServerEvents.ClearError) + } + ) + } + } + Spacer(Modifier.height(8.dp)) + Text( + text = stringResource(StringR.string.server_selection_server_footer), + modifier = Modifier.padding(horizontal = 16.dp), + style = ElementTextStyles.Regular.caption1, + textAlign = TextAlign.Start, + color = MaterialTheme.colorScheme.tertiary, + ) + Spacer(Modifier.height(32.dp)) + Button( + onClick = { eventSink(ChangeServerEvents.Submit) }, + enabled = interactionEnabled && state.submitEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.changeServerContinue) + ) { + Text(text = stringResource(id = StringR.string.login_continue), style = ElementTextStyles.Button) + } + if (state.changeServerAction is Async.Success) { + onChangeServerSuccess() + } + } + if (state.changeServerAction is Async.Loading) { + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center) + ) + } + } + } +} + +@Composable +internal fun ChangeServerErrorDialog(error: Throwable, onDismiss: () -> Unit) { + ErrorDialog( + content = error.localizedMessage ?: stringResource(id = StringR.string.unknown_error), + onDismiss = onDismiss + ) +} + +@Composable +internal fun SlidingSyncNotSupportedDialog(onLearnMoreClicked: () -> Unit, onDismiss: () -> Unit) { + ConfirmationDialog( + onDismiss = onDismiss, + submitText = stringResource(StringR.string.action_learn_more), + onSubmitClicked = onLearnMoreClicked, + title = stringResource(StringR.string.server_selection_sliding_sync_alert_title), + content = stringResource(StringR.string.server_selection_sliding_sync_alert_message), + ) } @Preview @@ -191,5 +267,5 @@ internal fun ChangeServerViewDarkPreview(@PreviewParameter(ChangeServerStateProv @Composable private fun ContentToPreview(state: ChangeServerState) { - ChangeServerView(state = state) + ChangeServerView(state = state, onBackPressed = {}, onLearnMoreClicked = {}) } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootEvents.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootEvents.kt index 01f33aa7d5..d3f8738dc7 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootEvents.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootEvents.kt @@ -17,8 +17,8 @@ package io.element.android.features.login.impl.root sealed interface LoginRootEvents { - object RefreshHomeServer : LoginRootEvents data class SetLogin(val login: String) : LoginRootEvents data class SetPassword(val password: String) : LoginRootEvents object Submit : LoginRootEvents + object ClearError : LoginRootEvents } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootNode.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootNode.kt index 3eff826db9..43070ba0da 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootNode.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootNode.kt @@ -47,16 +47,11 @@ class LoginRootNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val state = presenter.present() - OnLifecycleEvent { _, event -> - when (event) { - Lifecycle.Event.ON_RESUME -> state.eventSink(LoginRootEvents.RefreshHomeServer) - else -> Unit - } - } LoginRootScreen( state = state, modifier = modifier, onChangeServer = this::onChangeHomeServer, + onBackPressed = this::navigateUp ) } } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt index da2787da6d..d2117956c1 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt @@ -18,25 +18,30 @@ package io.element.android.features.login.impl.root import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable +import io.element.android.features.login.util.LoginConstants import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.data.tryOrNull -import io.element.android.libraries.matrix.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import javax.inject.Inject class LoginRootPresenter @Inject constructor(private val authenticationService: MatrixAuthenticationService) : Presenter { + private val defaultHomeserver = MatrixHomeServerDetails(LoginConstants.DEFAULT_HOMESERVER_URL, true, null) + @Composable override fun present(): LoginRootState { val localCoroutineScope = rememberCoroutineScope() - val homeserver = rememberSaveable { - mutableStateOf(authenticationService.getHomeserverOrDefault()) - } + val homeserver = authenticationService.getHomeserverDetails().collectAsState().value ?: defaultHomeserver val loggedInState: MutableState = remember { mutableStateOf(LoggedInState.NotLoggedIn) } @@ -46,19 +51,19 @@ class LoginRootPresenter @Inject constructor(private val authenticationService: fun handleEvents(event: LoginRootEvents) { when (event) { - LoginRootEvents.RefreshHomeServer -> refreshHomeServer(homeserver) is LoginRootEvents.SetLogin -> updateFormState(formState) { copy(login = event.login) } is LoginRootEvents.SetPassword -> updateFormState(formState) { copy(password = event.password) } - LoginRootEvents.Submit -> localCoroutineScope.submit(homeserver.value, formState.value, loggedInState) + LoginRootEvents.Submit -> localCoroutineScope.submit(homeserver.url, formState.value, loggedInState) + LoginRootEvents.ClearError -> loggedInState.value = LoggedInState.NotLoggedIn } } return LoginRootState( - homeserver = homeserver.value, + homeserverDetails = homeserver, loggedInState = loggedInState.value, formState = formState.value, eventSink = ::handleEvents @@ -83,7 +88,4 @@ class LoginRootPresenter @Inject constructor(private val authenticationService: formState.value = updateLambda(formState.value) } - private fun refreshHomeServer(homeserver: MutableState) { - homeserver.value = authenticationService.getHomeserverOrDefault() - } } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootScreen.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootScreen.kt index 310ee53936..4581dc1367 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootScreen.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootScreen.kt @@ -16,222 +16,332 @@ package io.element.android.features.login.impl.root +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ChevronRight +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import io.element.android.features.login.impl.error.loginError +import io.element.android.libraries.designsystem.ElementTextStyles +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.BackButton import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.theme.components.OutlinedTextField +import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.matrix.core.SessionId +import io.element.android.libraries.designsystem.theme.components.TextField +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.R as StringR +@OptIn(ExperimentalMaterial3Api::class) @Composable fun LoginRootScreen( state: LoginRootState, modifier: Modifier = Modifier, onChangeServer: () -> Unit = {}, onLoginWithSuccess: (SessionId) -> Unit = {}, + onBackPressed: () -> Unit, ) { - val eventSink = state.eventSink - Box( - modifier = modifier - .fillMaxSize() - .systemBarsPadding() - .imePadding() - ) { - val scrollState = rememberScrollState() - var loginFieldState by textFieldState(stateValue = state.formState.login) - var passwordFieldState by textFieldState(stateValue = state.formState.password) - - Column( - modifier = Modifier - .verticalScroll( - state = scrollState, - ) - .padding(horizontal = 16.dp), - ) { - val isError = state.loggedInState is LoggedInState.ErrorLoggingIn - // Title - Text( - text = stringResource(id = StringR.string.ftue_auth_welcome_back_title), - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 48.dp), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - fontSize = 24.sp, - color = MaterialTheme.colorScheme.primary, + val interactionEnabled by remember(state.loggedInState) { + derivedStateOf { + state.loggedInState != LoggedInState.LoggingIn + } + } + Scaffold( + topBar = { + TopAppBar( + title = {}, + navigationIcon = { BackButton(action = onBackPressed, enabled = interactionEnabled) }, ) - // Form - Column( - // modifier = Modifier.weight(1f), - ) { - Box( - modifier = Modifier.fillMaxWidth() - ) { - OutlinedTextField( - value = state.homeserver, - modifier = Modifier.fillMaxWidth(), - onValueChange = { /* no op */ }, - enabled = false, - label = { - Text(text = "Server") - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Uri, - ), - ) - Button( - onClick = onChangeServer, - modifier = Modifier - .align(Alignment.CenterEnd) - .testTag(TestTags.loginChangeServer) - .padding(top = 8.dp, end = 8.dp), - content = { - Text(text = "Change") - } - ) - } - OutlinedTextField( - value = loginFieldState, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginEmailUsername) - .padding(top = 60.dp), - label = { - Text(text = stringResource(id = StringR.string.login_signin_username_hint)) - }, - onValueChange = { - loginFieldState = it - eventSink(LoginRootEvents.SetLogin(it)) - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Email, - imeAction = ImeAction.Next - ), - ) - var passwordVisible by remember { mutableStateOf(false) } - if (state.loggedInState is LoggedInState.LoggingIn) { - // Ensure password is hidden when user submits the form - passwordVisible = false - } - OutlinedTextField( - value = passwordFieldState, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginPassword) - .padding(top = 24.dp), - onValueChange = { - passwordFieldState = it - eventSink(LoginRootEvents.SetPassword(it)) - }, - label = { - Text(text = "Password") - }, - isError = isError, - visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), - trailingIcon = { - val image = - if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff - val description = - if (passwordVisible) "Hide password" else "Show password" + } + ) { padding -> + Box( + modifier = modifier + .fillMaxSize() + .imePadding() + .padding(padding) + ) { + val scrollState = rememberScrollState() - IconButton(onClick = { passwordVisible = !passwordVisible }) { - Icon(imageVector = image, description) - } - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Password, - imeAction = ImeAction.Done, - ), - keyboardActions = KeyboardActions( - onDone = { eventSink(LoginRootEvents.Submit) } - ), - ) - if (state.loggedInState is LoggedInState.ErrorLoggingIn) { - Text( - text = loginError(state.formState, state.loggedInState.failure), - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp) - ) - } - } - // Submit - Button( - onClick = { eventSink(LoginRootEvents.Submit) }, - enabled = state.submitEnabled, + Column( modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginContinue) - .padding(vertical = 32.dp) + .verticalScroll(state = scrollState) + .padding(horizontal = 16.dp), ) { - Text(text = "Continue") + Spacer(Modifier.height(16.dp)) + // Title + Text( + text = stringResource(id = StringR.string.ftue_auth_welcome_back_title), + modifier = Modifier + .fillMaxWidth(), + style = ElementTextStyles.Bold.title1, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(Modifier.height(32.dp)) + + ChangeServerSection( + interactionEnabled = interactionEnabled, + homeserver = state.homeserverDetails.url, + onChangeServer = onChangeServer + ) + + Spacer(Modifier.height(32.dp)) + + LoginForm(state = state, interactionEnabled = interactionEnabled) + + Spacer(modifier = Modifier.height(32.dp)) + } when (val loggedInState = state.loggedInState) { is LoggedInState.LoggedIn -> onLoginWithSuccess(loggedInState.sessionId) else -> Unit } - } - if (state.loggedInState is LoggedInState.LoggingIn) { - CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) - ) + + if (state.loggedInState is LoggedInState.LoggingIn) { + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center) + ) + } } } } -@Preview @Composable -internal fun LoginRootScreenLightPreview() = ElementPreviewLight { ContentToPreview() } - -@Preview -@Composable -internal fun LoginRootScreenDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun ChangeServerSection( + interactionEnabled: Boolean, + homeserver: String, + onChangeServer: () -> Unit, + modifier: Modifier = Modifier +) { + Column(modifier) { + Text( + modifier = Modifier.padding(start = 16.dp, bottom = 8.dp), + text = stringResource(id = StringR.string.ftue_auth_sign_in_choose_server_header), + style = ElementTextStyles.Regular.formHeader, + ) + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(14.dp)) + .background(MaterialTheme.colorScheme.surfaceVariant) + .testTag(TestTags.loginChangeServer) + .clickable { + if (interactionEnabled) { + onChangeServer() + } + }, + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = homeserver, + style = ElementTextStyles.Bold.body, + textAlign = TextAlign.Start, + modifier = Modifier + .weight(1f) + .padding(horizontal = 16.dp, vertical = 16.dp) + ) + IconButton( + modifier = Modifier.size(24.dp), + onClick = { + if (interactionEnabled) { + onChangeServer() + } + } + ) { + Icon(imageVector = Icons.Default.ChevronRight, contentDescription = null, tint = MaterialTheme.colorScheme.tertiary) + } + Spacer(Modifier.width(8.dp)) + } + } +} @Composable -private fun ContentToPreview() { - LoginRootScreen( - state = aLoginRootState().copy( - homeserver = "matrix.org", - ), +internal fun LoginForm( + state: LoginRootState, + interactionEnabled: Boolean, + modifier: Modifier = Modifier +) { + var loginFieldState by textFieldState(stateValue = state.formState.login) + var passwordFieldState by textFieldState(stateValue = state.formState.password) + + val focusManager = LocalFocusManager.current + val eventSink = state.eventSink + Column(modifier) { + Text( + text = stringResource(StringR.string.login_form_title), + modifier = Modifier.padding(start = 16.dp), + style = ElementTextStyles.Regular.formHeader + ) + + Spacer(modifier = Modifier.height(8.dp)) + TextField( + value = loginFieldState, + readOnly = !interactionEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginEmailUsername) + .onTabOrEnterKeyFocusNext(focusManager), + label = { + Text(text = stringResource(StringR.string.ex_login_username_hint)) + }, + onValueChange = { + loginFieldState = it + eventSink(LoginRootEvents.SetLogin(it)) + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Email, + imeAction = ImeAction.Next + ), + keyboardActions = KeyboardActions(onNext = { + focusManager.moveFocus(FocusDirection.Down) + }), + singleLine = true, + maxLines = 1, + trailingIcon = if (loginFieldState.isNotEmpty()) { + { + IconButton(onClick = { + loginFieldState = "" + }) { + Icon(imageVector = Icons.Filled.Close, contentDescription = stringResource(StringR.string.action_clear)) + } + } + } else null, + ) + + var passwordVisible by remember { mutableStateOf(false) } + if (state.loggedInState is LoggedInState.LoggingIn) { + // Ensure password is hidden when user submits the form + passwordVisible = false + } + Spacer(Modifier.height(20.dp)) + TextField( + value = passwordFieldState, + readOnly = !interactionEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginPassword) + .onTabOrEnterKeyFocusNext(focusManager), + onValueChange = { + passwordFieldState = it + eventSink(LoginRootEvents.SetPassword(it)) + }, + label = { + Text(text = stringResource(StringR.string.login_signup_password_hint)) + }, + visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + val image = + if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff + val description = + if (passwordVisible) stringResource(StringR.string.login_hide_password) else stringResource(StringR.string.login_show_password) + + IconButton(onClick = { passwordVisible = !passwordVisible }) { + Icon(imageVector = image, description) + } + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions( + onDone = { eventSink(LoginRootEvents.Submit) } + ), + singleLine = true, + maxLines = 1, + ) + + if (state.loggedInState is LoggedInState.ErrorLoggingIn) { + LoginErrorDialog(error = state.loggedInState.failure, onDismiss = { + eventSink(LoginRootEvents.ClearError) + }) + } + Spacer(Modifier.height(28.dp)) + + // Submit + Button( + onClick = { eventSink(LoginRootEvents.Submit) }, + enabled = interactionEnabled && state.submitEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginContinue) + ) { + Text(text = stringResource(StringR.string.login_continue), style = ElementTextStyles.Button) + } + } +} + +@Composable +internal fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) { + ErrorDialog( + content = error.localizedMessage ?: stringResource(id = StringR.string.unknown_error), + onDismiss = onDismiss + ) +} + +@Preview +@Composable +internal fun LoginRootScreenLightPreview(@PreviewParameter(LoginRootStateProvider::class) state: LoginRootState) = + ElementPreviewLight { ContentToPreview(state) } + +@Preview +@Composable +internal fun LoginRootScreenDarkPreview(@PreviewParameter(LoginRootStateProvider::class) state: LoginRootState) = + ElementPreviewDark { ContentToPreview(state) } + +@Composable +private fun ContentToPreview(state: LoginRootState) { + LoginRootScreen( + state = state, + onBackPressed = {} ) } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt index 39a54b96f4..61e92d13d8 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt @@ -17,11 +17,12 @@ package io.element.android.features.login.impl.root import android.os.Parcelable -import io.element.android.libraries.matrix.core.SessionId +import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.parcelize.Parcelize data class LoginRootState( - val homeserver: String, + val homeserverDetails: MatrixHomeServerDetails, val loggedInState: LoggedInState, val formState: LoginFormState, val eventSink: (LoginRootEvents) -> Unit diff --git a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt index 11d6f10f16..f95140a91d 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt @@ -16,8 +16,24 @@ package io.element.android.features.login.impl.root +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.api.core.SessionId + +open class LoginRootStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aLoginRootState(), + aLoginRootState().copy(homeserverDetails = MatrixHomeServerDetails("some-custom-server.com", true, null)), + aLoginRootState().copy(formState = LoginFormState("user", "pass")), + aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.LoggingIn), + aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.ErrorLoggingIn(Throwable())), + aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.LoggedIn(SessionId("1234"))), + ) +} + fun aLoginRootState() = LoginRootState( - homeserver = "", + homeserverDetails = MatrixHomeServerDetails("matrix.org", true, null), loggedInState = LoggedInState.NotLoggedIn, formState = LoginFormState.Default, eventSink = {} diff --git a/features/login/src/main/kotlin/io/element/android/features/login/util/LoginConstants.kt b/features/login/src/main/kotlin/io/element/android/features/login/util/LoginConstants.kt new file mode 100644 index 0000000000..61bd6455f9 --- /dev/null +++ b/features/login/src/main/kotlin/io/element/android/features/login/util/LoginConstants.kt @@ -0,0 +1,24 @@ +/* + * 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.features.login.util + +object LoginConstants { + + const val DEFAULT_HOMESERVER_URL = "matrix.org" + const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md" + +} diff --git a/features/login/src/main/res/drawable/ic_homeserver.xml b/features/login/src/main/res/drawable/ic_homeserver.xml new file mode 100644 index 0000000000..ee061f7007 --- /dev/null +++ b/features/login/src/main/res/drawable/ic_homeserver.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/features/login/src/test/kotlin/io/element/android/features/login/changeserver/ChangeServerPresenterTest.kt b/features/login/src/test/kotlin/io/element/android/features/login/changeserver/ChangeServerPresenterTest.kt index 6cab31bbb7..fa12718bb3 100644 --- a/features/login/src/test/kotlin/io/element/android/features/login/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/src/test/kotlin/io/element/android/features/login/changeserver/ChangeServerPresenterTest.kt @@ -25,8 +25,11 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.login.impl.changeserver.ChangeServerEvents import io.element.android.features.login.impl.changeserver.ChangeServerPresenter import io.element.android.libraries.architecture.Async -import io.element.android.libraries.matrixtest.A_HOMESERVER -import io.element.android.libraries.matrixtest.auth.FakeAuthenticationService +import io.element.android.libraries.matrix.test.A_HOMESERVER +import io.element.android.libraries.matrix.test.A_HOMESERVER_URL +import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2 +import io.element.android.libraries.matrix.test.A_THROWABLE +import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -41,7 +44,23 @@ class ChangeServerPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.homeserver).isEqualTo(A_HOMESERVER) + assertThat(initialState.homeserver).isEqualTo(A_HOMESERVER_URL) + assertThat(initialState.submitEnabled).isTrue() + } + } + + @Test + fun `present - authentication service can provide a homeserver`() = runTest { + val presenter = ChangeServerPresenter( + FakeAuthenticationService().apply { + givenHomeserver(A_HOMESERVER.copy(url = A_HOMESERVER_URL_2)) + }, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.homeserver).isEqualTo(A_HOMESERVER_URL_2) assertThat(initialState.submitEnabled).isTrue() } } @@ -80,4 +99,70 @@ class ChangeServerPresenterTest { assertThat(successState.changeServerAction).isInstanceOf(Async.Success::class.java) } } + + @Test + fun `present - submit parses URL`() = runTest { + val presenter = ChangeServerPresenter( + FakeAuthenticationService(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val longUrl = "https://matrix.org/.well-known/" + val initialState = awaitItem() + initialState.eventSink.invoke(ChangeServerEvents.SetServer(longUrl)) + awaitItem() + initialState.eventSink.invoke(ChangeServerEvents.Submit) + val loadingState = awaitItem() + assertThat(loadingState.submitEnabled).isFalse() + assertThat(loadingState.changeServerAction).isInstanceOf(Async.Loading::class.java) + awaitItem() // Skip changing the url to the parsed domain + val successState = awaitItem() + assertThat(successState.submitEnabled).isTrue() + assertThat(successState.changeServerAction).isInstanceOf(Async.Success::class.java) + assertThat(successState.homeserver).isEqualTo("matrix.org") + } + } + + @Test + fun `present - submit fails`() = runTest { + val authServer = FakeAuthenticationService() + val presenter = ChangeServerPresenter(authServer) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + authServer.givenChangeServerError(Throwable()) + initialState.eventSink.invoke(ChangeServerEvents.Submit) + val failureState = awaitItem() + assertThat(failureState.submitEnabled).isTrue() + assertThat(failureState.changeServerAction).isInstanceOf(Async.Failure::class.java) + } + } + + @Test + fun `present - clear error`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = ChangeServerPresenter( + authenticationService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + // Submit will return an error + authenticationService.givenChangeServerError(A_THROWABLE) + initialState.eventSink(ChangeServerEvents.Submit) + + // Check an error was returned + val submittedState = awaitItem() + assertThat(submittedState.changeServerAction).isEqualTo(Async.Failure(A_THROWABLE)) + + // Assert the error is then cleared + submittedState.eventSink(ChangeServerEvents.ClearError) + val clearedState = awaitItem() + assertThat(clearedState.changeServerAction).isEqualTo(Async.Uninitialized) + } + } } diff --git a/features/login/src/test/kotlin/io/element/android/features/login/root/LoginRootPresenterTest.kt b/features/login/src/test/kotlin/io/element/android/features/login/root/LoginRootPresenterTest.kt index 6a69311438..ff1aac115a 100644 --- a/features/login/src/test/kotlin/io/element/android/features/login/root/LoginRootPresenterTest.kt +++ b/features/login/src/test/kotlin/io/element/android/features/login/root/LoginRootPresenterTest.kt @@ -26,14 +26,12 @@ import io.element.android.features.login.impl.root.LoggedInState import io.element.android.features.login.impl.root.LoginFormState import io.element.android.features.login.impl.root.LoginRootEvents import io.element.android.features.login.impl.root.LoginRootPresenter -import io.element.android.libraries.matrix.core.SessionId -import io.element.android.libraries.matrixtest.A_HOMESERVER -import io.element.android.libraries.matrixtest.A_HOMESERVER_2 -import io.element.android.libraries.matrixtest.A_PASSWORD -import io.element.android.libraries.matrixtest.A_SESSION_ID -import io.element.android.libraries.matrixtest.A_THROWABLE -import io.element.android.libraries.matrixtest.A_USER_NAME -import io.element.android.libraries.matrixtest.auth.FakeAuthenticationService +import io.element.android.libraries.matrix.test.A_HOMESERVER +import io.element.android.libraries.matrix.test.A_PASSWORD +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_THROWABLE +import io.element.android.libraries.matrix.test.A_USER_NAME +import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -48,7 +46,7 @@ class LoginRootPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.homeserver).isEqualTo(A_HOMESERVER) + assertThat(initialState.homeserverDetails).isEqualTo(A_HOMESERVER) assertThat(initialState.loggedInState).isEqualTo(LoggedInState.NotLoggedIn) assertThat(initialState.formState).isEqualTo(LoginFormState.Default) assertThat(initialState.submitEnabled).isFalse() @@ -92,7 +90,7 @@ class LoginRootPresenterTest { val submitState = awaitItem() assertThat(submitState.loggedInState).isEqualTo(LoggedInState.LoggingIn) val loggedInState = awaitItem() - assertThat(loggedInState.loggedInState).isEqualTo(LoggedInState.LoggedIn(SessionId(A_SESSION_ID))) + assertThat(loggedInState.loggedInState).isEqualTo(LoggedInState.LoggedIn(A_SESSION_ID)) } } @@ -120,7 +118,7 @@ class LoginRootPresenterTest { } @Test - fun `present - refresh server`() = runTest { + fun `present - clear error`() = runTest { val authenticationService = FakeAuthenticationService() val presenter = LoginRootPresenter( authenticationService, @@ -129,11 +127,20 @@ class LoginRootPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.homeserver).isEqualTo(A_HOMESERVER) - authenticationService.givenHomeserver(A_HOMESERVER_2) - initialState.eventSink.invoke(LoginRootEvents.RefreshHomeServer) - val refreshedState = awaitItem() - assertThat(refreshedState.homeserver).isEqualTo(A_HOMESERVER_2) + + // Submit will return an error + authenticationService.givenLoginError(A_THROWABLE) + initialState.eventSink(LoginRootEvents.Submit) + awaitItem() // Skip LoggingIn state + + // Check an error was returned + val submittedState = awaitItem() + assertThat(submittedState.loggedInState).isEqualTo(LoggedInState.ErrorLoggingIn(A_THROWABLE)) + + // Assert the error is then cleared + submittedState.eventSink(LoginRootEvents.ClearError) + val clearedState = awaitItem() + assertThat(clearedState.loggedInState).isEqualTo(LoggedInState.NotLoggedIn) } } } diff --git a/features/logout/build.gradle.kts b/features/logout/build.gradle.kts index 16581ba58a..e7be5fc95c 100644 --- a/features/logout/build.gradle.kts +++ b/features/logout/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { anvil(projects.anvilcodegen) implementation(projects.libraries.architecture) implementation(projects.libraries.core) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.elementresources) implementation(projects.libraries.uiStrings) @@ -46,7 +46,7 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) + testImplementation(projects.libraries.matrix.test) androidTestImplementation(libs.test.junitext) } diff --git a/features/logout/src/main/kotlin/io/element/android/features/logout/LogoutPreferencePresenter.kt b/features/logout/src/main/kotlin/io/element/android/features/logout/LogoutPreferencePresenter.kt index 3aa626f1fa..441e603791 100644 --- a/features/logout/src/main/kotlin/io/element/android/features/logout/LogoutPreferencePresenter.kt +++ b/features/logout/src/main/kotlin/io/element/android/features/logout/LogoutPreferencePresenter.kt @@ -24,7 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.execute -import io.element.android.libraries.matrix.MatrixClient +import io.element.android.libraries.matrix.api.MatrixClient import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/features/logout/src/test/kotlin/io/element/android/features/logout/LogoutPreferencePresenterTest.kt b/features/logout/src/test/kotlin/io/element/android/features/logout/LogoutPreferencePresenterTest.kt index e84226b3e8..8e8f45e4de 100644 --- a/features/logout/src/test/kotlin/io/element/android/features/logout/LogoutPreferencePresenterTest.kt +++ b/features/logout/src/test/kotlin/io/element/android/features/logout/LogoutPreferencePresenterTest.kt @@ -23,9 +23,9 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.architecture.Async -import io.element.android.libraries.matrix.core.SessionId -import io.element.android.libraries.matrixtest.A_THROWABLE -import io.element.android.libraries.matrixtest.FakeMatrixClient +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_THROWABLE +import io.element.android.libraries.matrix.test.FakeMatrixClient import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -34,7 +34,7 @@ class LogoutPreferencePresenterTest { @Test fun `present - initial state`() = runTest { val presenter = LogoutPreferencePresenter( - FakeMatrixClient(SessionId("sessionId")), + FakeMatrixClient(A_SESSION_ID), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -47,7 +47,7 @@ class LogoutPreferencePresenterTest { @Test fun `present - logout`() = runTest { val presenter = LogoutPreferencePresenter( - FakeMatrixClient(SessionId("sessionId")), + FakeMatrixClient(A_SESSION_ID), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -63,7 +63,7 @@ class LogoutPreferencePresenterTest { @Test fun `present - logout with error`() = runTest { - val matrixClient = FakeMatrixClient(SessionId("sessionId")) + val matrixClient = FakeMatrixClient(A_SESSION_ID) val presenter = LogoutPreferencePresenter( matrixClient, ) diff --git a/features/messages/api/build.gradle.kts b/features/messages/api/build.gradle.kts index 10568c83c6..45b8f50fc3 100644 --- a/features/messages/api/build.gradle.kts +++ b/features/messages/api/build.gradle.kts @@ -25,16 +25,5 @@ android { } dependencies { - implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix) - - testImplementation(libs.test.junit) - testImplementation(libs.coroutines.test) - testImplementation(libs.molecule.runtime) - testImplementation(libs.test.truth) - testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) - - androidTestImplementation(libs.test.junitext) } diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index fe65a6dfbd..6bdde74dce 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -36,11 +36,11 @@ dependencies { api(projects.features.messages.api) implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.textcomposer) - implementation(projects.libraries.dateformatter) + implementation(projects.libraries.dateformatter.api) implementation(libs.coil.compose) implementation(libs.datetime) implementation(libs.accompanist.flowlayout) @@ -52,7 +52,8 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.dateformatter.test) androidTestImplementation(libs.test.junitext) ksp(libs.showkase.processor) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index a6316585f4..abef5e22f5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -36,7 +36,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.matrix.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index c2f90160da..33d6cb2b59 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -21,7 +21,7 @@ import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.textcomposer.MessageComposerState import io.element.android.features.messages.impl.timeline.TimelineState import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomId @Immutable data class MessagesState( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index e403791509..3cd929d591 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -24,7 +24,7 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemList import io.element.android.features.messages.impl.timeline.aTimelineState import io.element.android.libraries.core.data.StableCharSequence import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.textcomposer.MessageComposerMode open class MessagesStateProvider : PreviewParameterProvider { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt index 43d8cafd3b..a5cc266e02 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt @@ -25,7 +25,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.data.StableCharSequence import io.element.android.libraries.core.data.toStableCharSequence -import io.element.android.libraries.matrix.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt index bf953f5883..ff64441198 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.impl.timeline -import io.element.android.libraries.matrix.core.EventId +import io.element.android.libraries.matrix.api.core.EventId sealed interface TimelineEvents { object LoadMore : TimelineEvents diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 8e4d7f77de..1d931c76bb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -26,9 +26,9 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.room.MatrixRoom -import io.element.android.libraries.matrix.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt index eb21b12fc1..0e86614bf3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt @@ -18,8 +18,8 @@ package io.element.android.features.messages.impl.timeline import androidx.compose.runtime.Immutable import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import kotlinx.collections.immutable.ImmutableList @Immutable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index 53712fb928..bb487845f7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -23,8 +23,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemReac import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt index a151c4c7e6..b9dd6590fc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt @@ -53,8 +53,8 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.matrix.permalink.PermalinkData -import io.element.android.libraries.matrix.permalink.PermalinkParser +import io.element.android.libraries.matrix.api.permalink.PermalinkData +import io.element.android.libraries.matrix.api.permalink.PermalinkParser import kotlinx.collections.immutable.persistentMapOf import org.jsoup.nodes.Document import org.jsoup.nodes.Element diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt index 142ee69608..4a78447bd7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt @@ -17,7 +17,7 @@ package io.element.android.features.messages.impl.timeline.diff import androidx.recyclerview.widget.DiffUtil -import io.element.android.libraries.matrix.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem internal class MatrixTimelineItemsDiffCallback( private val oldList: List, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index a04e003a1b..44b49cbae0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -23,7 +23,7 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index ecf52d44f4..0f9f216699 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -16,14 +16,14 @@ package io.element.android.features.messages.impl.timeline.factories.event -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent import io.element.android.features.messages.impl.timeline.util.toHtmlDocument -import io.element.android.libraries.matrix.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MediaResolver import org.matrix.rustcomponents.sdk.Message import org.matrix.rustcomponents.sdk.MessageType import javax.inject.Inject diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index 7856494c6b..39c8719db4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -22,7 +22,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemGrou import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.matrix.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.ProfileTimelineDetails import javax.inject.Inject diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt index 4a6dd5fdf1..5192f190db 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt @@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.timeline.factories.virtual import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel -import io.element.android.libraries.dateformatter.DaySeparatorFormatter +import io.element.android.libraries.dateformatter.api.DaySeparatorFormatter import org.matrix.rustcomponents.sdk.VirtualTimelineItem import javax.inject.Inject diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt index b22ba1ed83..b71a911086 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt @@ -21,7 +21,7 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemReadMarkerModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemUnknownVirtualModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel -import io.element.android.libraries.matrix.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import org.matrix.rustcomponents.sdk.VirtualTimelineItem import javax.inject.Inject diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt index e6963235fc..fbadbf79af 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt @@ -20,7 +20,7 @@ import androidx.compose.runtime.Immutable import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.EventId +import io.element.android.libraries.matrix.api.core.EventId @Immutable sealed interface TimelineItem { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt index 7dbfc5ccfb..8013ca8a65 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.impl.timeline.model.event -import io.element.android.libraries.matrix.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MediaResolver data class TimelineItemImageContent( val body: String, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt index 4b2de362fa..3aa9e4902f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt @@ -17,7 +17,7 @@ package io.element.android.features.messages.impl.timeline.model.event import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.matrix.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MediaResolver open class TimelineItemImageContentProvider : PreviewParameterProvider { override val values: Sequence diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 97192985f1..64e41caed4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -22,18 +22,18 @@ 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.features.messages.impl.actionlist.ActionListPresenter -import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.fixtures.aMessageEvent import io.element.android.features.messages.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.MessagesEvents import io.element.android.features.messages.impl.MessagesPresenter +import io.element.android.features.messages.impl.actionlist.ActionListPresenter +import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.textcomposer.MessageComposerPresenter import io.element.android.features.messages.impl.timeline.TimelinePresenter -import io.element.android.libraries.matrix.room.MatrixRoom -import io.element.android.libraries.matrixtest.AN_EVENT_ID -import io.element.android.libraries.matrixtest.A_ROOM_ID -import io.element.android.libraries.matrixtest.room.FakeMatrixRoom +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt index aefe06f438..7bb9583f05 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt @@ -32,10 +32,10 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrixtest.AN_EVENT_ID -import io.element.android.libraries.matrixtest.A_MESSAGE -import io.element.android.libraries.matrixtest.A_USER_ID -import io.element.android.libraries.matrixtest.A_USER_NAME +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_MESSAGE +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_NAME import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt index 68cfc1150c..08720eac06 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt @@ -21,10 +21,10 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemReac import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrixtest.AN_EVENT_ID -import io.element.android.libraries.matrixtest.A_MESSAGE -import io.element.android.libraries.matrixtest.A_USER_ID -import io.element.android.libraries.matrixtest.A_USER_NAME +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_MESSAGE +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_NAME import kotlinx.collections.immutable.persistentListOf internal fun aMessageEvent( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt index 75ba0ff070..404e50a3bd 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt @@ -16,7 +16,6 @@ package io.element.android.features.messages.fixtures -import io.element.android.features.messages.fakes.FakeDaySeparatorFormatter import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseMessageFactory @@ -31,6 +30,7 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemEventFactory import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemDaySeparatorFactory import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory +import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter internal fun aTimelineItemsFactory() = TimelineItemsFactory( dispatchers = testCoroutineDispatchers(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index 5782e08416..105d2ce31a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -27,12 +27,12 @@ import io.element.android.features.messages.impl.textcomposer.MessageComposerEve import io.element.android.features.messages.impl.textcomposer.MessageComposerPresenter import io.element.android.features.messages.impl.textcomposer.MessageComposerState import io.element.android.libraries.core.data.StableCharSequence -import io.element.android.libraries.matrixtest.ANOTHER_MESSAGE -import io.element.android.libraries.matrixtest.AN_EVENT_ID -import io.element.android.libraries.matrixtest.A_MESSAGE -import io.element.android.libraries.matrixtest.A_REPLY -import io.element.android.libraries.matrixtest.A_USER_NAME -import io.element.android.libraries.matrixtest.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.ANOTHER_MESSAGE +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_MESSAGE +import io.element.android.libraries.matrix.test.A_REPLY +import io.element.android.libraries.matrix.test.A_USER_NAME +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt index 94641cf314..24e6f77d32 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt @@ -25,9 +25,9 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelinePresenter -import io.element.android.libraries.matrixtest.AN_EVENT_ID -import io.element.android.libraries.matrixtest.room.FakeMatrixRoom -import io.element.android.libraries.matrixtest.timeline.FakeMatrixTimeline +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/preferences/build.gradle.kts b/features/preferences/build.gradle.kts index fadd8a7e05..4cface7d76 100644 --- a/features/preferences/build.gradle.kts +++ b/features/preferences/build.gradle.kts @@ -50,7 +50,7 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) + testImplementation(projects.libraries.matrix.test) androidTestImplementation(libs.test.junitext) ksp(libs.showkase.processor) diff --git a/features/preferences/src/test/kotlin/io/element/android/features/preferences/root/PreferencesRootPresenterTest.kt b/features/preferences/src/test/kotlin/io/element/android/features/preferences/root/PreferencesRootPresenterTest.kt index cf303c8384..9ca0f591a5 100644 --- a/features/preferences/src/test/kotlin/io/element/android/features/preferences/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/src/test/kotlin/io/element/android/features/preferences/root/PreferencesRootPresenterTest.kt @@ -26,7 +26,7 @@ import io.element.android.features.logout.LogoutPreferencePresenter import io.element.android.features.preferences.impl.root.PreferencesRootPresenter import io.element.android.features.rageshake.preferences.RageshakePreferencesPresenter import io.element.android.libraries.architecture.Async -import io.element.android.libraries.matrixtest.FakeMatrixClient +import io.element.android.libraries.matrix.test.FakeMatrixClient import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/rageshake/build.gradle.kts b/features/rageshake/build.gradle.kts index 37fb8e1d3f..cf16ccf1be 100644 --- a/features/rageshake/build.gradle.kts +++ b/features/rageshake/build.gradle.kts @@ -51,7 +51,7 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) + testImplementation(projects.libraries.matrix.test) testImplementation(libs.test.mockk) androidTestImplementation(libs.test.junitext) diff --git a/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/bugreport/BugReportPresenterTest.kt b/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/bugreport/BugReportPresenterTest.kt index 484c3bd1b0..93252c8891 100644 --- a/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/bugreport/BugReportPresenterTest.kt +++ b/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/bugreport/BugReportPresenterTest.kt @@ -25,7 +25,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.rageshake.crash.ui.A_CRASH_DATA import io.element.android.features.rageshake.crash.ui.FakeCrashDataStore import io.element.android.libraries.architecture.Async -import io.element.android.libraries.matrixtest.A_FAILURE_REASON +import io.element.android.libraries.matrix.test.A_FAILURE_REASON import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/bugreport/FakeBugReporter.kt b/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/bugreport/FakeBugReporter.kt index 29977d7a95..558edf0b15 100644 --- a/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/bugreport/FakeBugReporter.kt +++ b/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/bugreport/FakeBugReporter.kt @@ -19,7 +19,7 @@ package io.element.android.features.rageshake.bugreport import io.element.android.features.rageshake.reporter.BugReporter import io.element.android.features.rageshake.reporter.BugReporterListener import io.element.android.features.rageshake.reporter.ReportType -import io.element.android.libraries.matrixtest.A_FAILURE_REASON +import io.element.android.libraries.matrix.test.A_FAILURE_REASON import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch diff --git a/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/detection/RageshakeDetectionPresenterTest.kt b/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/detection/RageshakeDetectionPresenterTest.kt index 1ef8941bff..2993ab41f1 100644 --- a/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/detection/RageshakeDetectionPresenterTest.kt +++ b/features/rageshake/src/test/kotlin/io/element/android/features/rageshake/detection/RageshakeDetectionPresenterTest.kt @@ -28,7 +28,7 @@ import io.element.android.features.rageshake.preferences.FakeRageShake import io.element.android.features.rageshake.preferences.FakeRageshakeDataStore import io.element.android.features.rageshake.preferences.RageshakePreferencesPresenter import io.element.android.features.rageshake.screenshot.ImageResult -import io.element.android.libraries.matrixtest.AN_EXCEPTION +import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first diff --git a/features/roomlist/build.gradle.kts b/features/roomlist/build.gradle.kts index 60f8c83b3c..42818cd056 100644 --- a/features/roomlist/build.gradle.kts +++ b/features/roomlist/build.gradle.kts @@ -36,12 +36,12 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.elementresources) implementation(projects.libraries.uiStrings) - implementation(projects.libraries.dateformatter) + implementation(projects.libraries.dateformatter.api) implementation(libs.accompanist.placeholder) testImplementation(libs.test.junit) @@ -49,9 +49,8 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) - - testImplementation(testFixtures(projects.libraries.matrix)) + testImplementation(projects.libraries.matrix.test) + implementation(projects.libraries.dateformatter.test) androidTestImplementation(libs.test.junitext) diff --git a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/api/RoomListEntryPoint.kt b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/api/RoomListEntryPoint.kt index 5b5608c589..995da4498a 100644 --- a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/api/RoomListEntryPoint.kt +++ b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/api/RoomListEntryPoint.kt @@ -18,7 +18,7 @@ package io.element.android.features.roomlist.api import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomId interface RoomListEntryPoint : FeatureEntryPoint { interface Callback : Plugin { diff --git a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListNode.kt b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListNode.kt index de2ea0bfee..0d22e8ec08 100644 --- a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListNode.kt +++ b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListNode.kt @@ -27,7 +27,7 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.roomlist.api.RoomListEntryPoint import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomId @ContributesNode(SessionScope::class) class RoomListNode @AssistedInject constructor( diff --git a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index 52758eadf6..964a0157a3 100644 --- a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -27,11 +27,12 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.parallelMap -import io.element.android.libraries.dateformatter.LastMessageFormatter +import io.element.android.libraries.dateformatter.api.LastMessageFormatter import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.room.RoomSummary +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.RoomSummary import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -103,14 +104,14 @@ class RoomListPresenter @Inject constructor( val userDisplayName = client.loadUserDisplayName().getOrNull() val avatarData = AvatarData( - id = client.userId().value, + id = client.sessionId.value, name = userDisplayName, url = userAvatarUrl, size = AvatarSize.SMALL ) matrixUser.value = MatrixUser( - id = client.userId(), - username = userDisplayName ?: client.userId().value, + id = UserId(client.sessionId.value), + username = userDisplayName ?: client.sessionId.value, avatarData = avatarData, ) } diff --git a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListRoomSummary.kt b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListRoomSummary.kt index 44fcc5dc26..bec3253bff 100644 --- a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListRoomSummary.kt +++ b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListRoomSummary.kt @@ -18,7 +18,7 @@ package io.element.android.features.roomlist.impl import androidx.compose.runtime.Immutable import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomId @Immutable data class RoomListRoomSummary( diff --git a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListRoomSummaryProvider.kt b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListRoomSummaryProvider.kt index 9aef9fd732..c0f17f040d 100644 --- a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListRoomSummaryProvider.kt +++ b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListRoomSummaryProvider.kt @@ -18,7 +18,7 @@ package io.element.android.features.roomlist.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomId open class RoomListRoomSummaryProvider : PreviewParameterProvider { override val values: Sequence diff --git a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index 9b4b2e93ca..e057376363 100644 --- a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -18,7 +18,7 @@ package io.element.android.features.roomlist.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.UserId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index ee6a267190..5e440e2274 100644 --- a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -40,7 +40,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.utils.LogCompositions -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList diff --git a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index f8dd16a873..dd23bc36a8 100644 --- a/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -59,7 +59,7 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.LogCompositions -import io.element.android.libraries.matrix.core.UserId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import io.element.android.libraries.ui.strings.R as StringR diff --git a/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/FakeLastMessageFormatter.kt b/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/FakeLastMessageFormatter.kt index 997846056a..0040b9f480 100644 --- a/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/FakeLastMessageFormatter.kt +++ b/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/FakeLastMessageFormatter.kt @@ -16,7 +16,7 @@ package io.element.android.features.roomlist -import io.element.android.libraries.dateformatter.LastMessageFormatter +import io.element.android.libraries.dateformatter.api.LastMessageFormatter class FakeLastMessageFormatter : LastMessageFormatter { private var format = "" diff --git a/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/RoomListPresenterTests.kt b/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/RoomListPresenterTests.kt index b43efdab28..efb5dd4d5e 100644 --- a/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/RoomListPresenterTests.kt +++ b/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/RoomListPresenterTests.kt @@ -22,22 +22,22 @@ 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.features.roomlist.impl.RoomListPresenter import io.element.android.features.roomlist.impl.RoomListEvents +import io.element.android.features.roomlist.impl.RoomListPresenter import io.element.android.features.roomlist.impl.RoomListRoomSummary -import io.element.android.libraries.dateformatter.LastMessageFormatter +import io.element.android.libraries.dateformatter.api.LastMessageFormatter import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.SessionId -import io.element.android.libraries.matrixtest.AN_AVATAR_URL -import io.element.android.libraries.matrixtest.AN_EXCEPTION -import io.element.android.libraries.matrixtest.A_MESSAGE -import io.element.android.libraries.matrixtest.A_ROOM_ID -import io.element.android.libraries.matrixtest.A_ROOM_NAME -import io.element.android.libraries.matrixtest.A_USER_ID -import io.element.android.libraries.matrixtest.A_USER_NAME -import io.element.android.libraries.matrixtest.FakeMatrixClient -import io.element.android.libraries.matrixtest.room.FakeRoomSummaryDataSource -import io.element.android.libraries.matrixtest.room.aRoomSummaryFilled +import io.element.android.libraries.matrix.test.AN_AVATAR_URL +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_MESSAGE +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_ROOM_NAME +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_NAME +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource +import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -47,9 +47,7 @@ class RoomListPresenterTests { @Test fun `present - should start with no user and then load user with success`() = runTest { val presenter = RoomListPresenter( - FakeMatrixClient( - SessionId("sessionId") - ), + FakeMatrixClient(A_SESSION_ID), createDateFormatter() ) moleculeFlow(RecompositionClock.Immediate) { @@ -70,7 +68,7 @@ class RoomListPresenterTests { fun `present - should start with no user and then load user with error`() = runTest { val presenter = RoomListPresenter( FakeMatrixClient( - SessionId("sessionId"), + A_SESSION_ID, userDisplayName = Result.failure(AN_EXCEPTION), userAvatarURLString = Result.failure(AN_EXCEPTION), ), @@ -91,9 +89,7 @@ class RoomListPresenterTests { @Test fun `present - should filter room with success`() = runTest { val presenter = RoomListPresenter( - FakeMatrixClient( - SessionId("sessionId") - ), + FakeMatrixClient(A_SESSION_ID), createDateFormatter() ) moleculeFlow(RecompositionClock.Immediate) { @@ -113,7 +109,7 @@ class RoomListPresenterTests { val roomSummaryDataSource = FakeRoomSummaryDataSource() val presenter = RoomListPresenter( FakeMatrixClient( - sessionId = SessionId("sessionId"), + sessionId = A_SESSION_ID, roomSummaryDataSource = roomSummaryDataSource ), createDateFormatter() @@ -139,7 +135,7 @@ class RoomListPresenterTests { val roomSummaryDataSource = FakeRoomSummaryDataSource() val presenter = RoomListPresenter( FakeMatrixClient( - sessionId = SessionId("sessionId"), + sessionId = A_SESSION_ID, roomSummaryDataSource = roomSummaryDataSource ), createDateFormatter() @@ -170,7 +166,7 @@ class RoomListPresenterTests { val roomSummaryDataSource = FakeRoomSummaryDataSource() val presenter = RoomListPresenter( FakeMatrixClient( - sessionId = SessionId("sessionId"), + sessionId = A_SESSION_ID, roomSummaryDataSource = roomSummaryDataSource ), createDateFormatter() diff --git a/features/template/build.gradle.kts b/features/template/build.gradle.kts index 5dd2946f96..409a0c9bc5 100644 --- a/features/template/build.gradle.kts +++ b/features/template/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.elementresources) @@ -48,7 +48,7 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrixtest) + testImplementation(projects.libraries.matrix.test) androidTestImplementation(libs.test.junitext) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5f7f809c67..0e9c073b20 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,6 +42,7 @@ jsoup = "1.15.3" appyx = "1.0.3" dependencycheck = "7.4.4" stem = "2.2.3" +sqldelight = "1.5.5" # DI dagger = "2.44.2" @@ -69,6 +70,7 @@ androidx_lifecycle_runtime = { module = "androidx.lifecycle:lifecycle-runtime-kt androidx_lifecycle_compose = { module = "androidx.lifecycle:compose", version.ref = "lifecycle" } androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" } androidx_splash = "androidx.core:core-splashscreen:1.0.0" +androidx_security_crypto = "androidx.security:security-crypto:1.0.0" androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity_compose" } androidx_startup = { module = "androidx.startup:startup-runtime", version.ref = "startup" } @@ -119,6 +121,11 @@ appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.2" +sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } +sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } +sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" } +sqlcipher = "net.zetetic:android-database-sqlcipher:4.5.3" +sqlite = "androidx.sqlite:sqlite:2.3.0" # Di inject = "javax.inject:javax.inject:1" @@ -149,3 +156,4 @@ stemlibrary = { id = "com.likethesalad.stem-library", version.ref = "stem" } paparazzi = "app.cash.paparazzi:1.2.0" sonarqube = "org.sonarqube:3.5.0.2730" kover = "org.jetbrains.kotlinx.kover:0.6.1" +sqldelight = { id = "com.squareup.sqldelight", version.ref = "sqldelight" } diff --git a/libraries/dateformatter/.gitignore b/libraries/dateformatter/api/.gitignore similarity index 100% rename from libraries/dateformatter/.gitignore rename to libraries/dateformatter/api/.gitignore diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/LogTag.kt b/libraries/dateformatter/api/build.gradle.kts similarity index 71% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/LogTag.kt rename to libraries/dateformatter/api/build.gradle.kts index 85c1e6010e..45f1ada80a 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/LogTag.kt +++ b/libraries/dateformatter/api/build.gradle.kts @@ -14,6 +14,12 @@ * limitations under the License. */ -package io.element.android.libraries.matrix +// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed +@Suppress("DSL_SCOPE_VIOLATION") +plugins { + id("io.element.android-library") +} -internal const val LOG_TAG = "Matrix" +android { + namespace = "io.element.android.libraries.dateformatter.api" +} diff --git a/libraries/dateformatter/consumer-rules.pro b/libraries/dateformatter/api/consumer-rules.pro similarity index 100% rename from libraries/dateformatter/consumer-rules.pro rename to libraries/dateformatter/api/consumer-rules.pro diff --git a/libraries/dateformatter/proguard-rules.pro b/libraries/dateformatter/api/proguard-rules.pro similarity index 100% rename from libraries/dateformatter/proguard-rules.pro rename to libraries/dateformatter/api/proguard-rules.pro diff --git a/libraries/dateformatter/src/main/AndroidManifest.xml b/libraries/dateformatter/api/src/main/AndroidManifest.xml similarity index 100% rename from libraries/dateformatter/src/main/AndroidManifest.xml rename to libraries/dateformatter/api/src/main/AndroidManifest.xml diff --git a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/DaySeparatorFormatter.kt b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DaySeparatorFormatter.kt similarity index 92% rename from libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/DaySeparatorFormatter.kt rename to libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DaySeparatorFormatter.kt index 453bbb2154..682f0c5745 100644 --- a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/DaySeparatorFormatter.kt +++ b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DaySeparatorFormatter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.dateformatter +package io.element.android.libraries.dateformatter.api interface DaySeparatorFormatter { fun format(timestamp: Long): String diff --git a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/LastMessageFormatter.kt b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/LastMessageFormatter.kt similarity index 92% rename from libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/LastMessageFormatter.kt rename to libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/LastMessageFormatter.kt index caa5886cf9..7f61dfac5d 100644 --- a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/LastMessageFormatter.kt +++ b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/LastMessageFormatter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.dateformatter +package io.element.android.libraries.dateformatter.api interface LastMessageFormatter { fun format(timestamp: Long?): String diff --git a/libraries/dateformatter/impl/.gitignore b/libraries/dateformatter/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/libraries/dateformatter/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/libraries/dateformatter/build.gradle.kts b/libraries/dateformatter/impl/build.gradle.kts similarity index 86% rename from libraries/dateformatter/build.gradle.kts rename to libraries/dateformatter/impl/build.gradle.kts index 60ecd95052..3e7f698c1f 100644 --- a/libraries/dateformatter/build.gradle.kts +++ b/libraries/dateformatter/impl/build.gradle.kts @@ -27,17 +27,19 @@ anvil { } android { - namespace = "io.element.android.libraries.dateformatter" + namespace = "io.element.android.libraries.dateformatter.impl" dependencies { anvil(projects.anvilcodegen) implementation(libs.dagger) implementation(projects.libraries.di) implementation(projects.anvilannotations) + + implementation(projects.libraries.dateformatter.api) api(libs.datetime) - ksp(libs.showkase.processor) testImplementation(libs.test.junit) testImplementation(libs.test.truth) + testImplementation(projects.libraries.dateformatter.test) } } diff --git a/libraries/dateformatter/impl/consumer-rules.pro b/libraries/dateformatter/impl/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libraries/dateformatter/impl/proguard-rules.pro b/libraries/dateformatter/impl/proguard-rules.pro new file mode 100644 index 0000000000..ff59496d81 --- /dev/null +++ b/libraries/dateformatter/impl/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/libraries/matrixtest/src/main/AndroidManifest.xml b/libraries/dateformatter/impl/src/main/AndroidManifest.xml similarity index 91% rename from libraries/matrixtest/src/main/AndroidManifest.xml rename to libraries/dateformatter/impl/src/main/AndroidManifest.xml index 6a35d06cce..cf0e6386de 100644 --- a/libraries/matrixtest/src/main/AndroidManifest.xml +++ b/libraries/dateformatter/impl/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - - - - + diff --git a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt similarity index 95% rename from libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt rename to libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt index 2706343e6b..31b58085ba 100644 --- a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt @@ -39,17 +39,17 @@ class DateFormatters @Inject constructor( private val onlyTimeFormatter: DateTimeFormatter by lazy { val pattern = DateFormat.getBestDateTimePattern(locale, "HH:mm") ?: "HH:mm" - DateTimeFormatter.ofPattern(pattern) + DateTimeFormatter.ofPattern(pattern, locale) } private val dateWithMonthFormatter: DateTimeFormatter by lazy { val pattern = DateFormat.getBestDateTimePattern(locale, "d MMM") ?: "d MMM" - DateTimeFormatter.ofPattern(pattern) + DateTimeFormatter.ofPattern(pattern, locale) } private val dateWithYearFormatter: DateTimeFormatter by lazy { val pattern = DateFormat.getBestDateTimePattern(locale, "dd.MM.yyyy") ?: "dd.MM.yyyy" - DateTimeFormatter.ofPattern(pattern) + DateTimeFormatter.ofPattern(pattern, locale) } internal fun formatTime(localDateTime: LocalDateTime): String { diff --git a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultDaySeparatorFormatter.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultDaySeparatorFormatter.kt similarity index 94% rename from libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultDaySeparatorFormatter.kt rename to libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultDaySeparatorFormatter.kt index 29e2c87221..4c6ebbbde0 100644 --- a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultDaySeparatorFormatter.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultDaySeparatorFormatter.kt @@ -17,7 +17,7 @@ package io.element.android.libraries.dateformatter.impl import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.libraries.dateformatter.DaySeparatorFormatter +import io.element.android.libraries.dateformatter.api.DaySeparatorFormatter import io.element.android.libraries.di.AppScope import javax.inject.Inject diff --git a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatter.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatter.kt similarity index 95% rename from libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatter.kt rename to libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatter.kt index 753457c48f..fd7afe55e7 100644 --- a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatter.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatter.kt @@ -17,7 +17,7 @@ package io.element.android.libraries.dateformatter.impl import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.libraries.dateformatter.LastMessageFormatter +import io.element.android.libraries.dateformatter.api.LastMessageFormatter import io.element.android.libraries.di.AppScope import javax.inject.Inject diff --git a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt similarity index 100% rename from libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt rename to libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt diff --git a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/di/DateFormatterModule.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/di/DateFormatterModule.kt similarity index 94% rename from libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/di/DateFormatterModule.kt rename to libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/di/DateFormatterModule.kt index feab851a8b..352306da65 100644 --- a/libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/di/DateFormatterModule.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/di/DateFormatterModule.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.dateformatter.di +package io.element.android.libraries.dateformatter.impl.di import com.squareup.anvil.annotations.ContributesTo import dagger.Module diff --git a/libraries/dateformatter/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatterTest.kt b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatterTest.kt similarity index 96% rename from libraries/dateformatter/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatterTest.kt rename to libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatterTest.kt index cfe3e40fce..1d8390ec9f 100644 --- a/libraries/dateformatter/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatterTest.kt +++ b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatterTest.kt @@ -17,7 +17,8 @@ package io.element.android.libraries.dateformatter.impl import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.dateformatter.LastMessageFormatter +import io.element.android.libraries.dateformatter.api.LastMessageFormatter +import io.element.android.libraries.dateformatter.test.FakeClock import kotlinx.datetime.Instant import kotlinx.datetime.TimeZone import org.junit.Test diff --git a/libraries/dateformatter/test/.gitignore b/libraries/dateformatter/test/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/libraries/dateformatter/test/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/libraries/dateformatter/test/build.gradle.kts b/libraries/dateformatter/test/build.gradle.kts new file mode 100644 index 0000000000..afc7e66059 --- /dev/null +++ b/libraries/dateformatter/test/build.gradle.kts @@ -0,0 +1,30 @@ +/* + * 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. + */ + +// 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.libraries.dateformatter.test" + + dependencies { + implementation(projects.libraries.dateformatter.api) + api(libs.datetime) + } +} diff --git a/libraries/dateformatter/test/consumer-rules.pro b/libraries/dateformatter/test/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libraries/dateformatter/test/proguard-rules.pro b/libraries/dateformatter/test/proguard-rules.pro new file mode 100644 index 0000000000..ff59496d81 --- /dev/null +++ b/libraries/dateformatter/test/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/libraries/dateformatter/test/src/main/AndroidManifest.xml b/libraries/dateformatter/test/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..cf0e6386de --- /dev/null +++ b/libraries/dateformatter/test/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + diff --git a/libraries/dateformatter/src/test/kotlin/io/element/android/libraries/dateformatter/impl/FakeClock.kt b/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeClock.kt similarity index 94% rename from libraries/dateformatter/src/test/kotlin/io/element/android/libraries/dateformatter/impl/FakeClock.kt rename to libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeClock.kt index 58a5495218..4716b0f5f0 100644 --- a/libraries/dateformatter/src/test/kotlin/io/element/android/libraries/dateformatter/impl/FakeClock.kt +++ b/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeClock.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.dateformatter.impl +package io.element.android.libraries.dateformatter.test import kotlinx.datetime.Clock import kotlinx.datetime.Instant diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fakes/FakeDaySeparatorFormatter.kt b/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeDaySeparatorFormatter.kt similarity index 86% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/fakes/FakeDaySeparatorFormatter.kt rename to libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeDaySeparatorFormatter.kt index 40a52c640c..202e3ee5ae 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fakes/FakeDaySeparatorFormatter.kt +++ b/libraries/dateformatter/test/src/main/kotlin/io/element/android/libraries/dateformatter/test/FakeDaySeparatorFormatter.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package io.element.android.features.messages.fakes +package io.element.android.libraries.dateformatter.test -import io.element.android.libraries.dateformatter.DaySeparatorFormatter +import io.element.android.libraries.dateformatter.api.DaySeparatorFormatter class FakeDaySeparatorFormatter : DaySeparatorFormatter { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/Color.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/Color.kt index 279b38fdd1..61ec5a4a63 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/Color.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/Color.kt @@ -41,6 +41,22 @@ val SystemGrey5Dark = Color(0xFF2C2C2E) val SystemGrey6Light = Color(0xFFF2F2F7) val SystemGrey6Dark = Color(0xFF1C1C1E) +// For light themes +val Gray_25 = Color(0xFFF4F6FA) +val Gray_50 = Color(0xFFE3E8F0) +val Gray_100 = Color(0xFFC1C6CD) +val Gray_150 = Color(0xFF8D97A5) +val Gray_200 = Color(0xFF737D8C) +val Black_900 = Color(0xFF17191C) + +// For dark themes +val Gray_250 = Color(0xFFA9B2BC) +val Gray_300 = Color(0xFF8E99A4) +val Gray_400 = Color(0xFF6F7882) +val Gray_450 = Color(0xFF394049) +val Black_800 = Color(0xFF15191E) +val Black_950 = Color(0xFF21262C) + val Azure = Color(0xFF368BD6) val Kiwi = Color(0xFF74D12C) val Grape = Color(0xFFAC3BA8) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ElementTextStyles.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ElementTextStyles.kt index 4e0e3bd854..c23f9ff071 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ElementTextStyles.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ElementTextStyles.kt @@ -25,6 +25,14 @@ import androidx.compose.ui.unit.sp // TODO Remove object ElementTextStyles { + val Button = TextStyle( + fontSize = 17.sp, + fontWeight = FontWeight.Bold, + lineHeight = 22.sp, + fontStyle = FontStyle.Normal, + textAlign = TextAlign.Center, + ) + object Bold { val largeTitle = TextStyle( fontSize = 34.sp, @@ -180,6 +188,14 @@ object ElementTextStyles { textAlign = TextAlign.Center ) + val formHeader = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Normal, + lineHeight = 20.sp, + textAlign = TextAlign.Start + ) + val footnote = TextStyle( fontSize = 13.sp, fontWeight = FontWeight.Normal, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt index 265271085e..97e5a484f1 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt @@ -17,13 +17,12 @@ package io.element.android.libraries.designsystem.components.dialogs import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -31,10 +30,8 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.strings.R as StringR @@ -67,43 +64,24 @@ fun ConfirmationDialog( Text(content) }, dismissButton = { - Row( - modifier = Modifier.padding(all = 8.dp), - horizontalArrangement = Arrangement.Center - ) { - Column { - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { - onCancelClicked() - }) { - Text(cancelText) - } - if (thirdButtonText != null) { - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { - onThirdButtonClicked() - }) { - Text(thirdButtonText) - } + if (thirdButtonText != null) { + // If there is a 3rd item it should be at the end of the dialog + // Having this 3rd action is discouraged, see https://m3.material.io/components/dialogs/guidelines#e13b68f5-e367-4275-ad6f-c552ee8e358f + TextButton(onClick = onThirdButtonClicked) { + Text(thirdButtonText) } } + TextButton(onClick = onCancelClicked) { + Text(cancelText) } }, confirmButton = { - Row( - modifier = Modifier.padding(all = 8.dp), - horizontalArrangement = Arrangement.Center - ) { - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { - onSubmitClicked() - } - ) { - Text(submitText) + TextButton( + onClick = { + onSubmitClicked() } + ) { + Text(submitText) } }, shape = shape, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt index a7e4778cdd..d2f63e9c55 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -55,24 +56,14 @@ fun ErrorDialog( modifier = modifier, onDismissRequest = onDismiss, title = { - Text(text = title) + Text(title) }, text = { Text(content) }, confirmButton = { - Row( - modifier = Modifier.padding(all = 8.dp), - horizontalArrangement = Arrangement.Center - ) { - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { - onDismiss() - } - ) { - Text(submitText) - } + TextButton(onClick = onDismiss) { + Text(submitText) } }, shape = shape, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt index f79cfed70b..a4dc9faa4f 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt @@ -16,12 +16,9 @@ package io.element.android.libraries.designsystem.preview -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import io.element.android.libraries.designsystem.theme.ElementTheme +import io.element.android.libraries.designsystem.theme.components.Surface @Composable fun ElementPreviewLight( @@ -55,9 +52,8 @@ private fun ElementPreview( ) { ElementTheme(darkTheme = darkTheme) { if (showBackground) { - Box(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { - content() - } + // If we have a proper contentColor applied we need a Surface instead of a Box + Surface { content() } } else { content() } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsDark.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsDark.kt index 2a17b74521..22607a39c6 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsDark.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsDark.kt @@ -21,7 +21,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import io.element.android.libraries.designsystem.Azure +import io.element.android.libraries.designsystem.Black_800 +import io.element.android.libraries.designsystem.Black_950 import io.element.android.libraries.designsystem.DarkGrey +import io.element.android.libraries.designsystem.Gray_400 +import io.element.android.libraries.designsystem.Gray_450 import io.element.android.libraries.designsystem.SystemGrey5Dark import io.element.android.libraries.designsystem.SystemGrey6Dark import io.element.android.libraries.designsystem.theme.previews.ColorsSchemePreview @@ -30,6 +34,8 @@ fun elementColorsDark() = ElementColors( messageFromMeBackground = SystemGrey5Dark, messageFromOtherBackground = SystemGrey6Dark, messageHighlightedBackground = Azure, + quaternary = Gray_400, + quinary = Gray_450, isLight = false, ) @@ -48,12 +54,12 @@ val materialColorSchemeDark = darkColorScheme( // TODO onTertiary = ColorDarkTokens.OnTertiary, // TODO tertiaryContainer = ColorDarkTokens.TertiaryContainer, // TODO onTertiaryContainer = ColorDarkTokens.OnTertiaryContainer, - background = Color.Black, + background = Black_800, onBackground = Color.White, - surface = Color.Black, + surface = Black_800, onSurface = Color.White, - surfaceVariant = SystemGrey5Dark, - // TODO onSurfaceVariant = ColorDarkTokens.OnSurfaceVariant, + surfaceVariant = Black_950, + onSurfaceVariant = Color.White, // TODO surfaceTint = primary, // TODO inverseSurface = ColorDarkTokens.InverseSurface, // TODO inverseOnSurface = ColorDarkTokens.InverseOnSurface, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsLight.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsLight.kt index 2e69b5a7ff..637b6b4e3c 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsLight.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsLight.kt @@ -21,7 +21,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import io.element.android.libraries.designsystem.Azure -import io.element.android.libraries.designsystem.LightGrey +import io.element.android.libraries.designsystem.Black_900 +import io.element.android.libraries.designsystem.Gray_100 +import io.element.android.libraries.designsystem.Gray_150 +import io.element.android.libraries.designsystem.Gray_200 +import io.element.android.libraries.designsystem.Gray_25 +import io.element.android.libraries.designsystem.Gray_50 import io.element.android.libraries.designsystem.SystemGrey5Light import io.element.android.libraries.designsystem.SystemGrey6Light import io.element.android.libraries.designsystem.theme.previews.ColorsSchemePreview @@ -30,21 +35,23 @@ fun elementColorsLight() = ElementColors( messageFromMeBackground = SystemGrey5Light, messageFromOtherBackground = SystemGrey6Light, messageHighlightedBackground = Azure, + quaternary = Gray_100, + quinary = Gray_50, isLight = true, ) // TODO Lots of colors are missing val materialColorSchemeLight = lightColorScheme( - primary = Color.Black, + primary = Black_900, onPrimary = Color.White, // TODO primaryContainer = ColorLightTokens.PrimaryContainer, // TODO onPrimaryContainer = ColorLightTokens.OnPrimaryContainer, // TODO inversePrimary = ColorLightTokens.InversePrimary, - secondary = LightGrey, + secondary = Gray_200, // TODO onSecondary = ColorLightTokens.OnSecondary, // TODO secondaryContainer = ColorLightTokens.SecondaryContainer, // TODO onSecondaryContainer = ColorLightTokens.OnSecondaryContainer, - tertiary = Color.Black, + tertiary = Gray_150, // TODO onTertiary = ColorLightTokens.OnTertiary, // TODO tertiaryContainer = ColorLightTokens.TertiaryContainer, // TODO onTertiaryContainer = ColorLightTokens.OnTertiaryContainer, @@ -52,8 +59,8 @@ val materialColorSchemeLight = lightColorScheme( onBackground = Color.Black, surface = Color.White, onSurface = Color.Black, - surfaceVariant = SystemGrey5Light, - onSurfaceVariant = Color.Black, + surfaceVariant = Gray_25, + onSurfaceVariant = Gray_150, // TODO surfaceTint = primary, // TODO inverseSurface = ColorLightTokens.InverseSurface, // TODO inverseOnSurface = ColorLightTokens.InverseOnSurface, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementColors.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementColors.kt index b39c31d157..6aa489940d 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementColors.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementColors.kt @@ -16,15 +16,19 @@ package io.element.android.libraries.designsystem.theme +import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color +@Stable class ElementColors( messageFromMeBackground: Color, messageFromOtherBackground: Color, messageHighlightedBackground: Color, + quaternary: Color, + quinary: Color, isLight: Boolean, ) { var messageFromMeBackground by mutableStateOf(messageFromMeBackground) @@ -34,6 +38,12 @@ class ElementColors( var messageHighlightedBackground by mutableStateOf(messageHighlightedBackground) private set + var quaternary by mutableStateOf(quaternary) + private set + + var quinary by mutableStateOf(quinary) + private set + var isLight by mutableStateOf(isLight) private set @@ -41,11 +51,15 @@ class ElementColors( messageFromMeBackground: Color = this.messageFromMeBackground, messageFromOtherBackground: Color = this.messageFromOtherBackground, messageHighlightedBackground: Color = this.messageHighlightedBackground, + quaternary: Color = this.quaternary, + quinary: Color = this.quinary, isLight: Boolean = this.isLight, ) = ElementColors( messageFromMeBackground = messageFromMeBackground, messageFromOtherBackground = messageFromOtherBackground, messageHighlightedBackground = messageHighlightedBackground, + quaternary = quaternary, + quinary = quinary, isLight = isLight, ) @@ -53,6 +67,8 @@ class ElementColors( messageFromMeBackground = other.messageFromMeBackground messageFromOtherBackground = other.messageFromOtherBackground messageHighlightedBackground = other.messageHighlightedBackground + quaternary = other.quaternary + quinary = other.quinary isLight = other.isLight } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementTheme.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementTheme.kt index 545834ac3c..883346571a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementTheme.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementTheme.kt @@ -49,15 +49,16 @@ val LocalColors = staticCompositionLocalOf { elementColorsLight() } fun ElementTheme( darkTheme: Boolean = isSystemInDarkTheme(), dynamicColor: Boolean = false, /* true to enable MaterialYou */ - lightColors: ElementColors = elementColorsLight(), - darkColors: ElementColors = elementColorsDark(), + colors: ElementColors = if (darkTheme) elementColorsDark() else elementColorsLight(), materialLightColors: ColorScheme = materialColorSchemeLight, materialDarkColors: ColorScheme = materialColorSchemeDark, content: @Composable () -> Unit, ) { val systemUiController = rememberSystemUiController() val useDarkIcons = !darkTheme - val currentColor = remember(darkTheme) { if (darkTheme) darkColors else lightColors } + val currentColor = remember(darkTheme) { + colors.copy() + }.apply { updateColorsFrom(colors) } val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current @@ -75,9 +76,8 @@ fun ElementTheme( darkIcons = useDarkIcons ) } - val rememberedColors = remember { currentColor.copy() }.apply { updateColorsFrom(currentColor) } CompositionLocalProvider( - LocalColors provides rememberedColors, + LocalColors provides currentColor, ) { MaterialTheme( colorScheme = colorScheme, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BackButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BackButton.kt new file mode 100644 index 0000000000..8fb3989bd9 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BackButton.kt @@ -0,0 +1,62 @@ +/* + * 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.libraries.designsystem.theme.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.ui.strings.R.string as StringR + +@Composable +fun BackButton( + action: () -> Unit, + modifier: Modifier = Modifier, + icon: ImageVector = Icons.Default.ArrowBack, + contentDescription: String = stringResource(StringR.action_back), + enabled: Boolean = true +) { + IconButton( + modifier = modifier, + onClick = action, + enabled = enabled, + ) { + Icon(imageVector = icon, contentDescription = contentDescription) + } +} + +@Preview +@Composable +internal fun BackButtonPreviewLight() = ElementPreviewLight { ContentToPreview() } + +@Preview +@Composable +internal fun BackButtonPreviewDark() = ElementPreviewDark { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + Column { + BackButton(action = { }, enabled = true, contentDescription = "Back") + BackButton(action = { }, enabled = false, contentDescription = "Back") + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt index 235a97e2bb..716a42a422 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight @@ -37,11 +38,11 @@ fun Button( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, - shape: Shape = ButtonDefaults.shape, - colors: ButtonColors = ButtonDefaults.buttonColors(), - elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), + shape: Shape = ElementButtonDefaults.shape, + colors: ButtonColors = ElementButtonDefaults.buttonColors(), + elevation: ButtonElevation? = ElementButtonDefaults.buttonElevation(), border: BorderStroke? = null, - contentPadding: PaddingValues = ButtonDefaults.ContentPadding, + contentPadding: PaddingValues = ElementButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { @@ -59,6 +60,17 @@ fun Button( ) } +object ElementButtonDefaults { + val ContentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp) + val shape: Shape @Composable get() = ButtonDefaults.shape + @Composable + fun buttonElevation(): ButtonElevation = ButtonDefaults.buttonElevation() + + @Composable + fun buttonColors(): ButtonColors = ButtonDefaults.buttonColors() + +} + @Preview @Composable internal fun ButtonsLightPreview() = ElementPreviewLight { ContentToPreview() } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt index 72e2fb64e4..c5f11512ac 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt @@ -29,8 +29,17 @@ import androidx.compose.material3.TextFieldColors import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onPreviewKeyEvent +import androidx.compose.ui.input.key.type +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview @@ -40,7 +49,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.utils.allBooleans import io.element.android.libraries.designsystem.utils.asInt -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable fun OutlinedTextField( value: String, @@ -88,6 +97,16 @@ fun OutlinedTextField( ) } +@OptIn(ExperimentalComposeUiApi::class) +fun Modifier.onTabOrEnterKeyFocusNext(focusManager: FocusManager): Modifier = onPreviewKeyEvent { event -> + if (event.type == KeyEventType.KeyUp && (event.key == Key.Tab || event.key == Key.Enter)) { + focusManager.moveFocus(FocusDirection.Down) + true + } else { + false + } +} + @Preview @Composable internal fun OutlinedTextFieldsLightPreview() = ElementPreviewLight { ContentToPreview() } diff --git a/libraries/encrypted-db/build.gradle.kts b/libraries/encrypted-db/build.gradle.kts new file mode 100644 index 0000000000..e904072898 --- /dev/null +++ b/libraries/encrypted-db/build.gradle.kts @@ -0,0 +1,37 @@ +/* + * 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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.encrypteddb" + + buildTypes { + release { + isMinifyEnabled = true + consumerProguardFiles("consumer-proguard-rules.pro") + } + } +} + +dependencies { + implementation(libs.sqldelight.driver.android) + implementation(libs.sqlcipher) + implementation(libs.sqlite) + implementation(libs.androidx.security.crypto) +} diff --git a/libraries/encrypted-db/consumer-proguard-rules.pro b/libraries/encrypted-db/consumer-proguard-rules.pro new file mode 100644 index 0000000000..5d01f21e9b --- /dev/null +++ b/libraries/encrypted-db/consumer-proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +# Prevent ProGuard from renaming internal SQLCipher classes, which breaks the library. +# From https://github.com/sqlcipher/android-database-sqlcipher#proguard +-keep class net.sqlcipher.** { *; } diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt new file mode 100644 index 0000000000..5258c388c5 --- /dev/null +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.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.encrypteddb + +import android.content.Context +import com.squareup.sqldelight.android.AndroidSqliteDriver +import com.squareup.sqldelight.db.SqlDriver +import io.element.encrypteddb.passphrase.PassphraseProvider +import net.sqlcipher.database.SupportFactory + +/** + * Creates an encrypted version of the [SqlDriver] using SQLCipher's [SupportFactory]. + * @param passphraseProvider Provides the passphrase needed to use the SQLite database with SQLCipher. + */ +class SqlCipherDriverFactory( + private val passphraseProvider: PassphraseProvider, +) { + /** + * Returns a valid [SqlDriver] with SQLCipher support. + * @param schema The SQLite DB schema. + * @param name The name of the database to create. + * @param context Android [Context], used to instantiate the driver. + */ + fun create(schema: SqlDriver.Schema, name: String, context: Context): SqlDriver { + val passphrase = passphraseProvider.getPassphrase() + val factory = SupportFactory(passphrase) + return AndroidSqliteDriver(schema = schema, context = context, name = name, factory = factory) + } +} diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/PassphraseProvider.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/PassphraseProvider.kt new file mode 100644 index 0000000000..a12c5f9cfb --- /dev/null +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/PassphraseProvider.kt @@ -0,0 +1,27 @@ +/* + * 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.encrypteddb.passphrase + +/** + * An abstraction to implement secure providers for SQLCipher passphrases. + */ +interface PassphraseProvider { + /** + * Returns a passphrase for SQLCipher in [ByteArray] format. + */ + fun getPassphrase(): ByteArray +} diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt new file mode 100644 index 0000000000..3367c777d0 --- /dev/null +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt @@ -0,0 +1,59 @@ +/* + * 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.encrypteddb.passphrase + +import android.content.Context +import androidx.security.crypto.EncryptedFile +import java.io.File +import java.security.SecureRandom + +/** + * Provides a secure passphrase for SQLCipher by generating a random secret and storing it into an [EncryptedFile]. + * @param context Android [Context], used by [EncryptedFile] for cryptographic operations. + * @param file Destination file where the key will be stored. + * @param alias Alias of the key used to encrypt & decrypt the [EncryptedFile]'s contents. + * @param secretSize Length of the generated secret. + */ +class RandomSecretPassphraseProvider( + private val context: Context, + private val file: File, + private val alias: String, + private val secretSize: Int = 256, +) : PassphraseProvider { + + override fun getPassphrase(): ByteArray { + val encryptedFile = EncryptedFile.Builder( + file, + context, + alias, + EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB + ).build() + return if (!file.exists()) { + val secret = generateSecret() + encryptedFile.openFileOutput().use { it.write(secret) } + secret + } else { + encryptedFile.openFileInput().use { it.readBytes() } + } + } + + private fun generateSecret(): ByteArray { + val buffer = ByteArray(size = secretSize) + SecureRandom().nextBytes(buffer) + return buffer + } +} diff --git a/libraries/matrix/api/build.gradle.kts b/libraries/matrix/api/build.gradle.kts new file mode 100644 index 0000000000..6d622f249e --- /dev/null +++ b/libraries/matrix/api/build.gradle.kts @@ -0,0 +1,44 @@ +/* + * 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. + */ + +// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed +@Suppress("DSL_SCOPE_VIOLATION") +plugins { + id("io.element.android-library") + id("kotlin-parcelize") + alias(libs.plugins.anvil) + kotlin("plugin.serialization") version "1.8.10" +} + +android { + namespace = "io.element.android.libraries.matrix.api" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + // api(projects.libraries.rustsdk) + api(libs.matrix.sdk) + implementation(projects.libraries.di) + implementation(libs.dagger) + implementation(projects.libraries.core) + implementation("net.java.dev.jna:jna:5.13.0@aar") + implementation(libs.serialization.json) + api(projects.libraries.sessionStorage.api) + implementation(libs.coroutines.core) +} diff --git a/libraries/matrix/src/main/AndroidManifest.xml b/libraries/matrix/api/src/main/AndroidManifest.xml similarity index 100% rename from libraries/matrix/src/main/AndroidManifest.xml rename to libraries/matrix/api/src/main/AndroidManifest.xml diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt similarity index 74% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/MatrixClient.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 1d3fc62ff7..71d9e2a60c 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -14,14 +14,13 @@ * limitations under the License. */ -package io.element.android.libraries.matrix +package io.element.android.libraries.matrix.api -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.core.SessionId -import io.element.android.libraries.matrix.core.UserId -import io.element.android.libraries.matrix.media.MediaResolver -import io.element.android.libraries.matrix.room.MatrixRoom -import io.element.android.libraries.matrix.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource import org.matrix.rustcomponents.sdk.MediaSource import java.io.Closeable @@ -33,7 +32,6 @@ interface MatrixClient : Closeable { fun roomSummaryDataSource(): RoomSummaryDataSource fun mediaResolver(): MediaResolver suspend fun logout() - fun userId(): UserId suspend fun loadUserDisplayName(): Result suspend fun loadUserAvatarURLString(): Result suspend fun loadMediaContentForSource(source: MediaSource): Result diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/auth/MatrixAuthenticationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt similarity index 77% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/auth/MatrixAuthenticationService.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt index 4323e3f79b..80ee68e6de 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/auth/MatrixAuthenticationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt @@ -14,18 +14,18 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.auth +package io.element.android.libraries.matrix.api.auth -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.core.SessionId +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow interface MatrixAuthenticationService { fun isLoggedIn(): Flow suspend fun getLatestSessionId(): SessionId? suspend fun restoreSession(sessionId: SessionId): MatrixClient? - fun getHomeserver(): String? - fun getHomeserverOrDefault(): String + fun getHomeserverDetails(): StateFlow suspend fun setHomeserver(homeserver: String) suspend fun login(username: String, password: String): SessionId } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt new file mode 100644 index 0000000000..430029642e --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt @@ -0,0 +1,34 @@ +/* + * 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.libraries.matrix.api.auth + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import org.matrix.rustcomponents.sdk.HomeserverLoginDetails + +@Parcelize +data class MatrixHomeServerDetails( + val url: String, + val supportsPasswordLogin: Boolean, + val authenticationIssuer: String? +): Parcelable { + constructor(homeserverLoginDetails: HomeserverLoginDetails) : this( + homeserverLoginDetails.url(), + homeserverLoginDetails.supportsPasswordLogin(), + homeserverLoginDetails.authenticationIssuer() + ) +} diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/EventId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/EventId.kt similarity index 92% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/EventId.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/EventId.kt index 7ad4235e5e..5c94fa30e9 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/EventId.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/EventId.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.core +package io.element.android.libraries.matrix.api.core import java.io.Serializable diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/MatrixPatterns.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt similarity index 98% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/MatrixPatterns.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt index b2043d216b..f295fe81b9 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/MatrixPatterns.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.core +package io.element.android.libraries.matrix.api.core -import io.element.android.libraries.matrix.BuildConfig +import io.element.android.libraries.matrix.api.BuildConfig import timber.log.Timber /** diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/RoomId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomId.kt similarity index 92% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/RoomId.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomId.kt index 1a50c6a83a..6c556c8b62 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/RoomId.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomId.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.core +package io.element.android.libraries.matrix.api.core import java.io.Serializable diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/SessionId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SessionId.kt similarity index 80% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/SessionId.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SessionId.kt index bf1ce0c04b..6009aa5c03 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/SessionId.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SessionId.kt @@ -14,9 +14,6 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.core +package io.element.android.libraries.matrix.api.core -import java.io.Serializable - -@JvmInline -value class SessionId(val value: String) : Serializable +typealias SessionId = UserId diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/UserId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/UserId.kt similarity index 92% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/UserId.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/UserId.kt index 63f2c85216..00fbc6e928 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/core/UserId.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/UserId.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.core +package io.element.android.libraries.matrix.api.core import java.io.Serializable diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/media/MediaResolver.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaResolver.kt similarity index 95% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/media/MediaResolver.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaResolver.kt index 7adf22ccfc..c906c380f1 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/media/MediaResolver.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaResolver.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.media +package io.element.android.libraries.matrix.api.media import org.matrix.rustcomponents.sdk.MediaSource diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/MatrixToConverter.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverter.kt similarity index 97% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/MatrixToConverter.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverter.kt index 8ef0bbcd08..032f45def6 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/MatrixToConverter.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.permalink +package io.element.android.libraries.matrix.api.permalink import android.net.Uri diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/PermalinkData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt similarity index 96% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/PermalinkData.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt index b5f8b7fd92..0a70055e4f 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/PermalinkData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.permalink +package io.element.android.libraries.matrix.api.permalink import android.net.Uri diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/PermalinkParser.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt similarity index 98% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/PermalinkParser.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt index 71509850a1..fc900e4bbb 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/permalink/PermalinkParser.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.permalink +package io.element.android.libraries.matrix.api.permalink import android.net.Uri import android.net.UrlQuerySanitizer -import io.element.android.libraries.matrix.core.MatrixPatterns +import io.element.android.libraries.matrix.api.core.MatrixPatterns import timber.log.Timber import java.net.URLDecoder diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt similarity index 84% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/MatrixRoom.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 76a2a91882..f29770a171 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.room +package io.element.android.libraries.matrix.api.room -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import kotlinx.coroutines.flow.Flow interface MatrixRoom { @@ -46,5 +46,4 @@ interface MatrixRoom { suspend fun replyMessage(eventId: EventId, message: String): Result suspend fun redactEvent(eventId: EventId, reason: String? = null): Result - } diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt similarity index 91% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummary.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt index 8b922b322d..1a5d55861a 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummary.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.room +package io.element.android.libraries.matrix.api.room -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomId sealed interface RoomSummary { data class Empty(val identifier: String) : RoomSummary diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt new file mode 100644 index 0000000000..0e71dd2558 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt @@ -0,0 +1,24 @@ +/* + * 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.libraries.matrix.api.room + +import kotlinx.coroutines.flow.StateFlow + +interface RoomSummaryDataSource { + fun roomSummaries(): StateFlow> + fun setSlidingSyncRange(range: IntRange) +} diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/message/RoomMessage.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/message/RoomMessage.kt similarity index 80% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/message/RoomMessage.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/message/RoomMessage.kt index 3bfe5b5edb..e6f5ba3a5c 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/message/RoomMessage.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/message/RoomMessage.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.room.message +package io.element.android.libraries.matrix.api.room.message -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.core.UserId +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId data class RoomMessage( val eventId: EventId, diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt similarity index 92% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimeline.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt index 83831db882..091691b0ec 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.timeline +package io.element.android.libraries.matrix.api.timeline -import io.element.android.libraries.matrix.core.EventId +import io.element.android.libraries.matrix.api.core.EventId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimelineItem.kt similarity index 68% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimelineItem.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimelineItem.kt index 2d31d400a7..e85ceaadb6 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimelineItem.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimelineItem.kt @@ -14,11 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.timeline +package io.element.android.libraries.matrix.api.timeline -import io.element.android.libraries.matrix.core.EventId +import io.element.android.libraries.matrix.api.core.EventId import org.matrix.rustcomponents.sdk.EventTimelineItem -import org.matrix.rustcomponents.sdk.TimelineItem import org.matrix.rustcomponents.sdk.VirtualTimelineItem sealed interface MatrixTimelineItem { @@ -31,14 +30,3 @@ sealed interface MatrixTimelineItem { object Other : MatrixTimelineItem } -fun TimelineItem.asMatrixTimelineItem(): MatrixTimelineItem { - val asEvent = asEvent() - if (asEvent != null) { - return MatrixTimelineItem.Event(asEvent) - } - val asVirtual = asVirtual() - if (asVirtual != null) { - return MatrixTimelineItem.Virtual(asVirtual) - } - return MatrixTimelineItem.Other -} diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/tracing/TracingConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt similarity index 90% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/tracing/TracingConfiguration.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt index 747b93f287..8bcd602b9f 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/tracing/TracingConfiguration.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt @@ -14,9 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.tracing - -import timber.log.Timber +package io.element.android.libraries.matrix.api.tracing data class TracingConfiguration( val overrides: Map = emptyMap() @@ -74,12 +72,6 @@ sealed class LogLevel(val filter: String) { object Error : LogLevel("error") } -fun setupTracing(tracingConfiguration: TracingConfiguration) { - val filter = tracingConfiguration.filter - Timber.v("Tracing config filter = $filter") - org.matrix.rustcomponents.sdk.setupTracing(filter) -} - object TracingConfigurations { val release = TracingConfiguration(overrides = mapOf(Target.Common to LogLevel.Info)) val debug = TracingConfiguration(overrides = mapOf(Target.Common to LogLevel.Info)) diff --git a/libraries/matrix/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts similarity index 92% rename from libraries/matrix/build.gradle.kts rename to libraries/matrix/impl/build.gradle.kts index ac1e4f0aa1..49f18be870 100644 --- a/libraries/matrix/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -23,7 +23,7 @@ plugins { } android { - namespace = "io.element.android.libraries.matrix" + namespace = "io.element.android.libraries.matrix.impl" } anvil { @@ -34,6 +34,7 @@ dependencies { // api(projects.libraries.rustsdk) api(libs.matrix.sdk) implementation(projects.libraries.di) + implementation(projects.libraries.matrix.api) implementation(libs.dagger) implementation(projects.libraries.core) implementation("net.java.dev.jna:jna:5.13.0@aar") diff --git a/features/login/src/main/res/drawable/ic_baseline_dataset_24.xml b/libraries/matrix/impl/src/main/AndroidManifest.xml similarity index 57% rename from features/login/src/main/res/drawable/ic_baseline_dataset_24.xml rename to libraries/matrix/impl/src/main/AndroidManifest.xml index 9391d552f4..dc2b81fddc 100644 --- a/features/login/src/main/res/drawable/ic_baseline_dataset_24.xml +++ b/libraries/matrix/impl/src/main/AndroidManifest.xml @@ -1,3 +1,4 @@ + - - - + + + + + diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt similarity index 87% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/RustMatrixClient.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 4d12f104a0..e75dd756a2 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -14,21 +14,20 @@ * limitations under the License. */ -package io.element.android.libraries.matrix +package io.element.android.libraries.matrix.impl import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.core.SessionId -import io.element.android.libraries.matrix.core.UserId -import io.element.android.libraries.matrix.media.MediaResolver -import io.element.android.libraries.matrix.media.RustMediaResolver -import io.element.android.libraries.matrix.room.MatrixRoom -import io.element.android.libraries.matrix.room.RoomSummaryDataSource -import io.element.android.libraries.matrix.room.RustMatrixRoom -import io.element.android.libraries.matrix.room.RustRoomSummaryDataSource -import io.element.android.libraries.matrix.session.SessionStore -import io.element.android.libraries.matrix.session.sessionId -import io.element.android.libraries.matrix.sync.SlidingSyncObserverProxy +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.impl.media.RustMediaResolver +import io.element.android.libraries.matrix.impl.room.RustMatrixRoom +import io.element.android.libraries.matrix.impl.room.RustRoomSummaryDataSource +import io.element.android.libraries.matrix.impl.sync.SlidingSyncObserverProxy +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client @@ -51,7 +50,7 @@ class RustMatrixClient constructor( private val baseDirectory: File, ) : MatrixClient { - override val sessionId: SessionId = client.session().sessionId() + override val sessionId: UserId = UserId(client.userId()) private val clientDelegate = object : ClientDelegate { override fun didReceiveAuthError(isSoftLogout: Boolean) { @@ -174,11 +173,9 @@ class RustMatrixClient constructor( Timber.e(failure, "Fail to call logout on HS. Still delete local files.") } baseDirectory.deleteSessionDirectory(userID = client.userId()) - sessionStore.reset() + sessionStore.removeSession(client.userId()) } - override fun userId(): UserId = UserId(client.userId()) - override suspend fun loadUserDisplayName(): Result = withContext(dispatchers.io) { runCatching { client.displayName() diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt similarity index 58% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/auth/RustMatrixAuthenticationService.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 82bd9391b1..7a51386d02 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -14,28 +14,36 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.auth +package io.element.android.libraries.matrix.impl.auth import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.AppScope -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.RustMatrixClient -import io.element.android.libraries.matrix.core.SessionId -import io.element.android.libraries.matrix.session.SessionStore -import io.element.android.libraries.matrix.session.sessionId -import io.element.android.libraries.matrix.util.logError +import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.impl.RustMatrixClient +import io.element.android.libraries.matrix.impl.util.logError +import io.element.android.libraries.sessionstorage.api.SessionData +import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.AuthenticationService import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder +import org.matrix.rustcomponents.sdk.Session import timber.log.Timber import java.io.File import javax.inject.Inject @ContributesBinding(AppScope::class) +@SingleIn(AppScope::class) class RustMatrixAuthenticationService @Inject constructor( private val baseDirectory: File, private val coroutineScope: CoroutineScope, @@ -44,23 +52,25 @@ class RustMatrixAuthenticationService @Inject constructor( private val authService: AuthenticationService, ) : MatrixAuthenticationService { + private var currentHomeserver = MutableStateFlow(null) + override fun isLoggedIn(): Flow { return sessionStore.isLoggedIn() } override suspend fun getLatestSessionId(): SessionId? = withContext(coroutineDispatchers.io) { - sessionStore.getLatestSession()?.sessionId() + sessionStore.getLatestSession()?.userId?.let { UserId(it) } } override suspend fun restoreSession(sessionId: SessionId) = withContext(coroutineDispatchers.io) { - sessionStore.getSession(sessionId) - ?.let { session -> + sessionStore.getSession(sessionId.value) + ?.let { sessionData -> try { ClientBuilder() .basePath(baseDirectory.absolutePath) - .username(session.userId) + .username(sessionData.userId) .build().apply { - restoreSession(session) + restoreSession(sessionData.toSession()) } } catch (throwable: Throwable) { logError(throwable) @@ -71,13 +81,15 @@ class RustMatrixAuthenticationService @Inject constructor( } } - override fun getHomeserver(): String? = authService.homeserverDetails()?.url() - - override fun getHomeserverOrDefault(): String = getHomeserver() ?: "matrix.org" + override fun getHomeserverDetails(): StateFlow = currentHomeserver override suspend fun setHomeserver(homeserver: String) { withContext(coroutineDispatchers.io) { authService.configureHomeserver(homeserver) + val homeServerDetails = authService.homeserverDetails()?.use { MatrixHomeServerDetails(it) } + if (homeServerDetails != null) { + currentHomeserver.value = homeServerDetails.copy(url = homeserver) + } } } @@ -90,8 +102,8 @@ class RustMatrixAuthenticationService @Inject constructor( throw failure } val session = client.session() - sessionStore.storeData(session) - session.sessionId() + sessionStore.storeData(session.toSessionData()) + SessionId(session.userId) } private fun createMatrixClient(client: Client): MatrixClient { @@ -104,3 +116,23 @@ class RustMatrixAuthenticationService @Inject constructor( ) } } + +private fun SessionData.toSession() = Session( + accessToken = accessToken, + refreshToken = refreshToken, + userId = userId, + deviceId = deviceId, + homeserverUrl = homeserverUrl, + isSoftLogout = isSoftLogout, + slidingSyncProxy = slidingSyncProxy, +) + +private fun Session.toSessionData() = SessionData( + userId = userId, + deviceId = deviceId, + accessToken = accessToken, + refreshToken = refreshToken, + homeserverUrl = homeserverUrl, + isSoftLogout = isSoftLogout, + slidingSyncProxy = slidingSyncProxy, +) diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/di/MatrixModule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/MatrixModule.kt similarity index 95% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/di/MatrixModule.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/MatrixModule.kt index 5cb654faf7..c6919e78d5 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/di/MatrixModule.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/MatrixModule.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.di +package io.element.android.libraries.matrix.impl.di import com.squareup.anvil.annotations.ContributesTo import dagger.Module diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/media/RustMediaResolver.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt similarity index 75% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/media/RustMediaResolver.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt index bdc5b85203..413ee26f67 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/media/RustMediaResolver.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt @@ -14,9 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.media +package io.element.android.libraries.matrix.impl.media -import io.element.android.libraries.matrix.MatrixClient +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.media.MediaResolver import org.matrix.rustcomponents.sdk.mediaSourceFromUrl internal class RustMediaResolver(private val client: MatrixClient) : MediaResolver { @@ -28,13 +29,14 @@ internal class RustMediaResolver(private val client: MatrixClient) : MediaResolv } override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? { - if (meta.source == null) return null - return when (meta.kind) { - is MediaResolver.Kind.Content -> client.loadMediaContentForSource(meta.source) + val source = meta.source ?: return null + val kind = meta.kind + return when (kind) { + is MediaResolver.Kind.Content -> client.loadMediaContentForSource(source) is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnailForSource( - meta.source, - meta.kind.width.toLong(), - meta.kind.height.toLong() + source, + kind.width.toLong(), + kind.height.toLong() ) }.getOrNull() } diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummaryDetailsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt similarity index 87% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummaryDetailsFactory.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt index d295f9848e..26a14a3b86 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummaryDetailsFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt @@ -14,10 +14,11 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.room +package io.element.android.libraries.matrix.impl.room -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.room.message.RoomMessageFactory +import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.RoomSummaryDetails import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.SlidingSyncRoom diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt similarity index 92% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RustMatrixRoom.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 99c21931c4..46d7ff4b1b 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -14,13 +14,14 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.room +package io.element.android.libraries.matrix.impl.room import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.timeline.MatrixTimeline -import io.element.android.libraries.matrix.timeline.RustMatrixTimeline +import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter @@ -32,7 +33,6 @@ import org.matrix.rustcomponents.sdk.SlidingSyncRoom import org.matrix.rustcomponents.sdk.UpdateSummary import org.matrix.rustcomponents.sdk.genTransactionId import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown -import timber.log.Timber class RustMatrixRoom( private val slidingSyncUpdateFlow: Flow, diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummaryDataSource.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt similarity index 95% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummaryDataSource.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt index 7bbc70fdd0..cae0515f5a 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomSummaryDataSource.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt @@ -14,11 +14,13 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.room +package io.element.android.libraries.matrix.impl.room import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.sync.roomListDiff -import io.element.android.libraries.matrix.sync.state +import io.element.android.libraries.matrix.impl.sync.roomListDiff +import io.element.android.libraries.matrix.impl.sync.state +import io.element.android.libraries.matrix.api.room.RoomSummary +import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel @@ -39,11 +41,6 @@ import timber.log.Timber import java.io.Closeable import java.util.UUID -interface RoomSummaryDataSource { - fun roomSummaries(): StateFlow> - fun setSlidingSyncRange(range: IntRange) -} - internal class RustRoomSummaryDataSource( private val slidingSyncUpdateFlow: Flow, private val slidingSync: SlidingSync, diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/message/RoomMessageFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/message/RoomMessageFactory.kt similarity index 81% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/message/RoomMessageFactory.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/message/RoomMessageFactory.kt index ebeb1d6eb0..84fa57659e 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/message/RoomMessageFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/message/RoomMessageFactory.kt @@ -14,10 +14,11 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.room.message +package io.element.android.libraries.matrix.impl.room.message -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.core.UserId +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.message.RoomMessage import org.matrix.rustcomponents.sdk.EventTimelineItem class RoomMessageFactory { diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/sync/SlidingSyncObserverProxy.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SlidingSyncObserverProxy.kt similarity index 96% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/sync/SlidingSyncObserverProxy.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SlidingSyncObserverProxy.kt index 1922eb985f..6c62171a65 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/sync/SlidingSyncObserverProxy.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SlidingSyncObserverProxy.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.sync +package io.element.android.libraries.matrix.impl.sync import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/sync/SlidingSyncViewFlows.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SlidingSyncViewFlows.kt similarity index 94% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/sync/SlidingSyncViewFlows.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SlidingSyncViewFlows.kt index a3d323b8d0..8665705095 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/sync/SlidingSyncViewFlows.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SlidingSyncViewFlows.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.sync +package io.element.android.libraries.matrix.impl.sync -import io.element.android.libraries.matrix.util.mxCallbackFlow +import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimelineDiffProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt similarity index 95% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimelineDiffProcessor.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt index afea46f2ff..f3033a40e1 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/MatrixTimelineDiffProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt @@ -14,8 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.timeline +package io.element.android.libraries.matrix.impl.timeline +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItem.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItem.kt new file mode 100644 index 0000000000..70cbc165f7 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItem.kt @@ -0,0 +1,32 @@ +/* + * 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.libraries.matrix.impl.timeline + +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import org.matrix.rustcomponents.sdk.TimelineItem + +fun TimelineItem.asMatrixTimelineItem(): MatrixTimelineItem { + val asEvent = asEvent() + if (asEvent != null) { + return MatrixTimelineItem.Event(asEvent) + } + val asVirtual = asVirtual() + if (asVirtual != null) { + return MatrixTimelineItem.Virtual(asVirtual) + } + return MatrixTimelineItem.Other +} diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt similarity index 92% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/RustMatrixTimeline.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 60ccb7420b..98ea6e7d3f 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -14,12 +14,14 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.timeline +package io.element.android.libraries.matrix.impl.timeline import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.room.MatrixRoom -import io.element.android.libraries.matrix.util.TaskHandleBag +import io.element.android.libraries.matrix.impl.util.TaskHandleBag +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt new file mode 100644 index 0000000000..f32b18c9f2 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt @@ -0,0 +1,26 @@ +/* + * 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.libraries.matrix.impl.tracing + +import io.element.android.libraries.matrix.api.tracing.TracingConfiguration +import timber.log.Timber + +fun setupTracing(tracingConfiguration: TracingConfiguration) { + val filter = tracingConfiguration.filter + Timber.v("Tracing config filter = $filter") + org.matrix.rustcomponents.sdk.setupTracing(filter) +} diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/CallbackFlow.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt similarity index 95% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/CallbackFlow.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt index 9cf36f1db1..a347973e89 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/CallbackFlow.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.util +package io.element.android.libraries.matrix.impl.util import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/Error.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Error.kt similarity index 94% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/Error.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Error.kt index d480262f81..4910445bee 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/Error.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Error.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.util +package io.element.android.libraries.matrix.impl.util import org.matrix.rustcomponents.sdk.ClientException import timber.log.Timber diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/TaskHandleBag.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt similarity index 95% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/TaskHandleBag.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt index f4f75d0e8e..ad2c6ccddd 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/util/TaskHandleBag.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.util +package io.element.android.libraries.matrix.impl.util import org.matrix.rustcomponents.sdk.TaskHandle import java.util.concurrent.CopyOnWriteArraySet diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/PreferencesSessionStore.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/PreferencesSessionStore.kt deleted file mode 100644 index 9468937266..0000000000 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/PreferencesSessionStore.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.libraries.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.libraries.di.AppScope -import io.element.android.libraries.di.ApplicationContext -import io.element.android.libraries.di.SingleIn -import io.element.android.libraries.matrix.core.SessionId -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, - val slidingSyncProxy: 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, - slidingSyncProxy = session.slidingSyncProxy - ) - 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, - slidingSyncProxy = sessionData.slidingSyncProxy - ) - } - } - - override suspend fun getSession(sessionId: SessionId): Session? { - //TODO we should have a proper session management - return getLatestSession() - } - - override suspend fun reset() { - store.edit { it.clear() } - } -} diff --git a/libraries/matrixtest/build.gradle.kts b/libraries/matrix/test/build.gradle.kts similarity index 91% rename from libraries/matrixtest/build.gradle.kts rename to libraries/matrix/test/build.gradle.kts index 39641e7eed..42b3658f20 100644 --- a/libraries/matrixtest/build.gradle.kts +++ b/libraries/matrix/test/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 New Vector Ltd + * 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. @@ -25,6 +25,6 @@ android { } dependencies { - api(projects.libraries.matrix) + api(projects.libraries.matrix.api) api(libs.coroutines.core) } diff --git a/libraries/matrix/test/src/main/AndroidManifest.xml b/libraries/matrix/test/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..dc2b81fddc --- /dev/null +++ b/libraries/matrix/test/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt similarity index 73% rename from libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/FakeMatrixClient.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index aedc0fd1a2..d5326fc725 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -14,23 +14,22 @@ * limitations under the License. */ -package io.element.android.libraries.matrixtest +package io.element.android.libraries.matrix.test -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.core.SessionId -import io.element.android.libraries.matrix.core.UserId -import io.element.android.libraries.matrix.media.MediaResolver -import io.element.android.libraries.matrix.room.MatrixRoom -import io.element.android.libraries.matrix.room.RoomSummaryDataSource -import io.element.android.libraries.matrixtest.media.FakeMediaResolver -import io.element.android.libraries.matrixtest.room.FakeMatrixRoom -import io.element.android.libraries.matrixtest.room.FakeRoomSummaryDataSource +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.test.media.FakeMediaResolver +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource import kotlinx.coroutines.delay import org.matrix.rustcomponents.sdk.MediaSource class FakeMatrixClient( - override val sessionId: SessionId = SessionId(A_SESSION_ID), + override val sessionId: SessionId = A_SESSION_ID, private val userDisplayName: Result = Result.success(A_USER_NAME), private val userAvatarURLString: Result = Result.success(AN_AVATAR_URL), val roomSummaryDataSource: RoomSummaryDataSource = FakeRoomSummaryDataSource() @@ -63,8 +62,6 @@ class FakeMatrixClient( logoutFailure?.let { throw it } } - override fun userId(): UserId = A_USER_ID - override suspend fun loadUserDisplayName(): Result { return userDisplayName } diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt similarity index 66% rename from libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/TestData.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index 970a3882ab..e44a85786d 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -14,16 +14,19 @@ * limitations under the License. */ -package io.element.android.libraries.matrixtest +package io.element.android.libraries.matrix.test -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.core.UserId +import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId const val A_USER_NAME = "alice" const val A_PASSWORD = "password" val A_USER_ID = UserId("@alice:server.org") +val A_SESSION_ID = SessionId(A_USER_ID.value) val A_ROOM_ID = RoomId("!aRoomId") val AN_EVENT_ID = EventId("\$anEventId") @@ -32,9 +35,10 @@ const val A_MESSAGE = "Hello world!" const val A_REPLY = "OK, I'll be there!" const val ANOTHER_MESSAGE = "Hello universe!" -const val A_HOMESERVER = "matrix.org" -const val A_HOMESERVER_2 = "matrix-client.org" -const val A_SESSION_ID = "sessionId" +const val A_HOMESERVER_URL = "matrix.org" +const val A_HOMESERVER_URL_2 = "matrix-client.org" + +val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, true, null) const val AN_AVATAR_URL = "mxc://data" diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/auth/FakeAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt similarity index 58% rename from libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/auth/FakeAuthenticationService.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt index ab4935ede3..102ec2d868 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/auth/FakeAuthenticationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt @@ -14,20 +14,24 @@ * limitations under the License. */ -package io.element.android.libraries.matrixtest.auth +package io.element.android.libraries.matrix.test.auth -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.auth.MatrixAuthenticationService -import io.element.android.libraries.matrix.core.SessionId -import io.element.android.libraries.matrixtest.A_HOMESERVER -import io.element.android.libraries.matrixtest.A_SESSION_ID +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.test.A_HOMESERVER +import io.element.android.libraries.matrix.test.A_USER_ID import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOf class FakeAuthenticationService : MatrixAuthenticationService { - private var homeserver: String = A_HOMESERVER + private var homeserver = MutableStateFlow(null) private var loginError: Throwable? = null + private var changeServerError: Throwable? = null override fun isLoggedIn(): Flow { return flowOf(false) @@ -41,29 +45,30 @@ class FakeAuthenticationService : MatrixAuthenticationService { return null } - override fun getHomeserver(): String? { - return null - } - - fun givenHomeserver(homeserver: String) { - this.homeserver = homeserver - } - - override fun getHomeserverOrDefault(): String { + override fun getHomeserverDetails(): StateFlow { return homeserver } + fun givenHomeserver(homeserver: MatrixHomeServerDetails) { + this.homeserver.value = homeserver + } + override suspend fun setHomeserver(homeserver: String) { + changeServerError?.let { throw it } delay(100) } override suspend fun login(username: String, password: String): SessionId { delay(100) loginError?.let { throw it } - return SessionId(A_SESSION_ID) + return A_USER_ID } fun givenLoginError(throwable: Throwable?) { loginError = throwable } + + fun givenChangeServerError(throwable: Throwable?) { + changeServerError = throwable + } } diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/media/FakeMediaResolver.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt similarity index 87% rename from libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/media/FakeMediaResolver.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt index ef171e5a09..c642c630f5 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/media/FakeMediaResolver.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package io.element.android.libraries.matrixtest.media +package io.element.android.libraries.matrix.test.media -import io.element.android.libraries.matrix.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MediaResolver class FakeMediaResolver : MediaResolver { override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt similarity index 84% rename from libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeMatrixRoom.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 3f5028291c..95eb07789e 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package io.element.android.libraries.matrixtest.room +package io.element.android.libraries.matrix.test.room -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.room.MatrixRoom -import io.element.android.libraries.matrix.timeline.MatrixTimeline -import io.element.android.libraries.matrixtest.A_ROOM_ID -import io.element.android.libraries.matrixtest.timeline.FakeMatrixTimeline +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeRoomSummaryDataSource.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt similarity index 86% rename from libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeRoomSummaryDataSource.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt index 9d7cb3e377..969ad4b413 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeRoomSummaryDataSource.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.matrixtest.room +package io.element.android.libraries.matrix.test.room -import io.element.android.libraries.matrix.room.RoomSummary -import io.element.android.libraries.matrix.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.room.RoomSummary +import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt similarity index 80% rename from libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/RoomSummaryFixture.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index 41d9f6d524..86e56a0bf3 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package io.element.android.libraries.matrixtest.room +package io.element.android.libraries.matrix.test.room -import io.element.android.libraries.matrix.core.RoomId -import io.element.android.libraries.matrix.room.RoomSummary -import io.element.android.libraries.matrix.room.RoomSummaryDetails -import io.element.android.libraries.matrixtest.A_MESSAGE -import io.element.android.libraries.matrixtest.A_ROOM_ID -import io.element.android.libraries.matrixtest.A_ROOM_NAME +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.RoomSummary +import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.test.A_MESSAGE +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_ROOM_NAME fun aRoomSummaryFilled( roomId: RoomId = A_ROOM_ID, diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/timeline/FakeMatrixTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt similarity index 91% rename from libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/timeline/FakeMatrixTimeline.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt index 1fdf17260f..bb5c0157b6 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/timeline/FakeMatrixTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.libraries.matrixtest.timeline +package io.element.android.libraries.matrix.test.timeline -import io.element.android.libraries.matrix.core.EventId -import io.element.android.libraries.matrix.timeline.MatrixTimeline -import io.element.android.libraries.matrix.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/libraries/matrixui/build.gradle.kts b/libraries/matrixui/build.gradle.kts index 39b2b677d8..c9dead5eb4 100644 --- a/libraries/matrixui/build.gradle.kts +++ b/libraries/matrixui/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { anvil(projects.anvilcodegen) implementation(projects.libraries.di) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.core) implementation(libs.coil.compose) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/MatrixItemHelper.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/MatrixItemHelper.kt index f60f95cee8..6a2d1b2a3c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/MatrixItemHelper.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/MatrixItemHelper.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.matrix.ui import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.matrix.MatrixClient +import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow @@ -38,13 +38,13 @@ class MatrixItemHelper @Inject constructor( val userDisplayName = client.loadUserDisplayName().getOrNull() val avatarData = AvatarData( - client.userId().value, + client.sessionId.value, userDisplayName, userAvatarUrl, avatarSize ) MatrixUser( - id = client.userId(), + id = client.sessionId, username = userDisplayName, avatarData = avatarData, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt index 9112a548a6..b19424c28b 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.matrix.ui.components import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.anAvatarData -import io.element.android.libraries.matrix.core.UserId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser open class MatrixUserProvider : PreviewParameterProvider { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt index 7ede975af5..f12842eff0 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt @@ -17,7 +17,7 @@ package io.element.android.libraries.matrix.ui.media import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MediaResolver import org.matrix.rustcomponents.sdk.mediaSourceFromUrl fun AvatarData.toMetadata(): MediaResolver.Meta { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt index 70a7ab2afb..cf89678318 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt @@ -20,7 +20,7 @@ import android.content.Context import coil.ImageLoader import coil.ImageLoaderFactory import io.element.android.libraries.di.ApplicationContext -import io.element.android.libraries.matrix.MatrixClient +import io.element.android.libraries.matrix.api.MatrixClient import javax.inject.Inject class LoggedInImageLoaderFactory @Inject constructor( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt index 22daed0851..fa0f42bf9b 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt @@ -21,8 +21,8 @@ import coil.fetch.FetchResult import coil.fetch.Fetcher import coil.request.Options import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.media.MediaResolver +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.media.MediaResolver import java.nio.ByteBuffer internal class MediaFetcher( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt index f390b352dc..7861649afe 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt @@ -19,7 +19,7 @@ package io.element.android.libraries.matrix.ui.media import coil.key.Keyer import coil.request.Options import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MediaResolver internal class AvatarKeyer : Keyer { override fun key(data: AvatarData, options: Options): String? { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/MatrixUser.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/MatrixUser.kt index b1a58c7920..c524cbb733 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/MatrixUser.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/MatrixUser.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.matrix.ui.model import androidx.compose.runtime.Immutable import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.core.UserId +import io.element.android.libraries.matrix.api.core.UserId @Immutable data class MatrixUser( diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/Session.kt b/libraries/session-storage/api/build.gradle.kts similarity index 73% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/Session.kt rename to libraries/session-storage/api/build.gradle.kts index a1943f273c..99fd07fee6 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/Session.kt +++ b/libraries/session-storage/api/build.gradle.kts @@ -14,9 +14,14 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.session +plugins { + id("io.element.android-library") +} -import io.element.android.libraries.matrix.core.SessionId -import org.matrix.rustcomponents.sdk.Session +android { + namespace = "io.element.android.libraries.sessionstorage.api" +} -fun Session.sessionId() = SessionId("${userId}_${deviceId}") +dependencies { + implementation(libs.coroutines.core) +} diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt new file mode 100644 index 0000000000..3f79701fd8 --- /dev/null +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt @@ -0,0 +1,27 @@ +/* + * 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.libraries.sessionstorage.api + +data class SessionData( + val userId: String, + val deviceId: String, + val accessToken: String, + val refreshToken: String?, + val homeserverUrl: String, + val isSoftLogout: Boolean, + val slidingSyncProxy: String? +) diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/SessionStore.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt similarity index 68% rename from libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/SessionStore.kt rename to libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt index 3ca806acee..de0ec2f727 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/SessionStore.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt @@ -14,16 +14,14 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.session +package io.element.android.libraries.sessionstorage.api -import io.element.android.libraries.matrix.core.SessionId import kotlinx.coroutines.flow.Flow -import org.matrix.rustcomponents.sdk.Session interface SessionStore { fun isLoggedIn(): Flow - suspend fun storeData(session: Session) - suspend fun getSession(sessionId: SessionId): Session? - suspend fun getLatestSession(): Session? - suspend fun reset() + suspend fun storeData(sessionData: SessionData) + suspend fun getSession(sessionId: String): SessionData? + suspend fun getLatestSession(): SessionData? + suspend fun removeSession(sessionId: String) } diff --git a/libraries/session-storage/impl-memory/build.gradle.kts b/libraries/session-storage/impl-memory/build.gradle.kts new file mode 100644 index 0000000000..ec1d618962 --- /dev/null +++ b/libraries/session-storage/impl-memory/build.gradle.kts @@ -0,0 +1,28 @@ +/* + * 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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.sessionstorage.impl.memory" +} + +dependencies { + implementation(projects.libraries.sessionStorage.api) + implementation(libs.coroutines.core) +} diff --git a/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemorySessionStore.kt b/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemorySessionStore.kt new file mode 100644 index 0000000000..b73ffdeb9a --- /dev/null +++ b/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemorySessionStore.kt @@ -0,0 +1,50 @@ +/* + * 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.libraries.sessionstorage.impl.memory + +import io.element.android.libraries.sessionstorage.api.SessionData +import io.element.android.libraries.sessionstorage.api.SessionStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map + +class InMemorySessionStore : SessionStore { + + private var sessionDataFlow = MutableStateFlow(null) + + override fun isLoggedIn(): Flow { + return sessionDataFlow.map { it != null } + } + + override suspend fun storeData(sessionData: SessionData) { + sessionDataFlow.value = sessionData + } + + override suspend fun getSession(sessionId: String): SessionData? { + return sessionDataFlow.value.takeIf { it?.userId == sessionId } + } + + override suspend fun getLatestSession(): SessionData? { + return sessionDataFlow.value + } + + override suspend fun removeSession(sessionId: String) { + if (sessionDataFlow.value?.userId == sessionId) { + sessionDataFlow.value = null + } + } +} diff --git a/libraries/session-storage/impl/build.gradle.kts b/libraries/session-storage/impl/build.gradle.kts new file mode 100644 index 0000000000..b1952b6cc5 --- /dev/null +++ b/libraries/session-storage/impl/build.gradle.kts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +plugins { + id("io.element.android-library") + alias(libs.plugins.anvil) + alias(libs.plugins.sqldelight) +} + +android { + namespace = "io.element.android.libraries.sessionstorage.impl" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + implementation(libs.dagger) + implementation(projects.libraries.core) + implementation(projects.libraries.encryptedDb) + implementation(projects.libraries.sessionStorage.api) + implementation(libs.sqldelight.driver.android) + implementation(libs.sqlcipher) + implementation(libs.sqlite) + implementation(libs.androidx.security.crypto) + implementation(projects.libraries.di) + implementation(libs.sqldelight.coroutines) + + testImplementation(libs.test.junit) + testImplementation(libs.test.truth) + testImplementation(libs.test.turbine) + testImplementation(libs.coroutines.test) + testImplementation(libs.sqldelight.driver.jvm) +} + +sqldelight { + database("SessionDatabase") {} +} diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt new file mode 100644 index 0000000000..6c32bcd1f3 --- /dev/null +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt @@ -0,0 +1,59 @@ +/* + * 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.libraries.sessionstorage.impl + +import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.sqldelight.runtime.coroutines.asFlow +import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.sessionstorage.api.SessionData +import io.element.android.libraries.sessionstorage.api.SessionStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class DatabaseSessionStore @Inject constructor( + private val database: SessionDatabase, +) : SessionStore { + + override fun isLoggedIn(): Flow { + return database.sessionDataQueries.selectFirst().asFlow().mapToOneOrNull().map { it != null } + } + + override suspend fun storeData(sessionData: SessionData) { + database.sessionDataQueries.insertSessionData(sessionData.toDbModel()) + } + + override suspend fun getLatestSession(): SessionData? { + return database.sessionDataQueries.selectFirst() + .executeAsOneOrNull() + ?.toApiModel() + } + + override suspend fun getSession(sessionId: String): SessionData? { + return database.sessionDataQueries.selectByUserId(sessionId) + .executeAsOneOrNull() + ?.toApiModel() + } + + override suspend fun removeSession(sessionId: String) { + database.sessionDataQueries.removeSession(sessionId) + } +} diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt new file mode 100644 index 0000000000..627f896381 --- /dev/null +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.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.libraries.sessionstorage.impl + +import io.element.android.libraries.sessionstorage.api.SessionData + +internal fun SessionData.toDbModel(): io.element.android.libraries.matrix.session.SessionData { + return io.element.android.libraries.matrix.session.SessionData( + userId = userId, + deviceId = deviceId, + accessToken = accessToken, + refreshToken = refreshToken, + homeserverUrl = homeserverUrl, + isSoftLogout = isSoftLogout, + slidingSyncProxy = slidingSyncProxy, + ) +} + +internal fun io.element.android.libraries.matrix.session.SessionData.toApiModel(): SessionData { + return SessionData( + userId = userId, + deviceId = deviceId, + accessToken = accessToken, + refreshToken = refreshToken, + homeserverUrl = homeserverUrl, + isSoftLogout = isSoftLogout, + slidingSyncProxy = slidingSyncProxy, + ) +} diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt new file mode 100644 index 0000000000..b101fc39b4 --- /dev/null +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.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.libraries.sessionstorage.impl.di + +import android.content.Context +import com.squareup.anvil.annotations.ContributesTo +import dagger.Module +import dagger.Provides +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.sessionstorage.impl.SessionDatabase +import io.element.encrypteddb.SqlCipherDriverFactory +import io.element.encrypteddb.passphrase.RandomSecretPassphraseProvider + +@Module +@ContributesTo(AppScope::class) +object SessionStorageModule { + @Provides + @SingleIn(AppScope::class) + fun provideMatrixDatabase(@ApplicationContext context: Context): SessionDatabase { + val name = "session_database" + val secretFile = context.getDatabasePath("$name.key") + val passphraseProvider = RandomSecretPassphraseProvider(context, secretFile, name) + val driver = SqlCipherDriverFactory(passphraseProvider) + .create(SessionDatabase.Schema, "$name.db", context) + return SessionDatabase(driver) + } +} diff --git a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq new file mode 100644 index 0000000000..c0731361e2 --- /dev/null +++ b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq @@ -0,0 +1,21 @@ +CREATE TABLE SessionData ( + userId TEXT NOT NULL PRIMARY KEY, + deviceId TEXT NOT NULL, + accessToken TEXT NOT NULL, + refreshToken TEXT, + homeserverUrl TEXT NOT NULL, + isSoftLogout INTEGER AS Boolean NOT NULL DEFAULT 0, + slidingSyncProxy TEXT +); + +selectFirst: +SELECT * FROM SessionData LIMIT 1; + +selectByUserId: +SELECT * FROM SessionData WHERE userId = ?; + +insertSessionData: +INSERT INTO SessionData(userId, deviceId, accessToken, refreshToken, homeserverUrl, isSoftLogout, slidingSyncProxy) VALUES ?; + +removeSession: +DELETE FROM SessionData WHERE userId = ?; diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt new file mode 100644 index 0000000000..5bd726609b --- /dev/null +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt @@ -0,0 +1,112 @@ +/* + * 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.libraries.sessionstorage.impl + +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver +import io.element.android.libraries.matrix.session.SessionData +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class DatabaseSessionStoreTests { + + private lateinit var database: SessionDatabase + private lateinit var databaseSessionStore: DatabaseSessionStore + + private val aSessionData = SessionData( + userId = "userId", + deviceId = "deviceId", + accessToken = "accessToken", + refreshToken = "refreshToken", + homeserverUrl = "homeserverUrl", + isSoftLogout = false, + slidingSyncProxy = null + ) + + @Before + fun setup() { + // Initialise in memory SQLite driver + val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + SessionDatabase.Schema.create(driver) + + database = SessionDatabase(driver) + databaseSessionStore = DatabaseSessionStore(database) + } + + @Test + fun `storeData persists the SessionData into the DB`() = runTest { + assertThat(database.sessionDataQueries.selectFirst().executeAsOneOrNull()).isNull() + + databaseSessionStore.storeData(aSessionData.toApiModel()) + + assertThat(database.sessionDataQueries.selectFirst().executeAsOneOrNull()).isEqualTo(aSessionData) + } + + @Test + fun `isLoggedIn emits true while there are sessions in the DB`() = runTest { + databaseSessionStore.isLoggedIn().test { + assertThat(awaitItem()).isFalse() + database.sessionDataQueries.insertSessionData(aSessionData) + assertThat(awaitItem()).isTrue() + database.sessionDataQueries.removeSession(aSessionData.userId) + assertThat(awaitItem()).isFalse() + } + } + + @Test + fun `getLatestSession gets the first session in the DB`() = runTest { + database.sessionDataQueries.insertSessionData(aSessionData) + database.sessionDataQueries.insertSessionData(aSessionData.copy(userId = "otherUserId")) + + val latestSession = databaseSessionStore.getLatestSession()?.toDbModel() + + assertThat(latestSession).isEqualTo(aSessionData) + } + + @Test + fun `getSession returns a matching session in DB if exists`() = runTest { + database.sessionDataQueries.insertSessionData(aSessionData) + database.sessionDataQueries.insertSessionData(aSessionData.copy(userId = "otherUserId")) + + val foundSession = databaseSessionStore.getSession(aSessionData.userId)?.toDbModel() + + assertThat(foundSession).isEqualTo(aSessionData) + } + + @Test + fun `getSession returns null if a no matching session exists in DB`() = runTest { + database.sessionDataQueries.insertSessionData(aSessionData.copy(userId = "otherUserId")) + + val foundSession = databaseSessionStore.getSession(aSessionData.userId) + + assertThat(foundSession).isNull() + } + + @Test + fun `removeSession removes the associated session in DB`() = runTest { + database.sessionDataQueries.insertSessionData(aSessionData) + + databaseSessionStore.removeSession(aSessionData.userId) + + assertThat(database.sessionDataQueries.selectByUserId(aSessionData.userId).executeAsOneOrNull()).isNull() + } + +} diff --git a/libraries/textcomposer/build.gradle.kts b/libraries/textcomposer/build.gradle.kts index 6f15fefa42..dca13603be 100644 --- a/libraries/textcomposer/build.gradle.kts +++ b/libraries/textcomposer/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) implementation(libs.wysiwyg) implementation(libs.androidx.constraintlayout) diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerMode.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerMode.kt index fed8265656..093fb865fa 100644 --- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerMode.kt +++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerMode.kt @@ -17,7 +17,7 @@ package io.element.android.libraries.textcomposer import android.os.Parcelable -import io.element.android.libraries.matrix.core.EventId +import io.element.android.libraries.matrix.api.core.EventId import kotlinx.parcelize.Parcelize sealed interface MessageComposerMode : Parcelable { diff --git a/libraries/ui-strings/src/main/res/values-ca/strings.xml b/libraries/ui-strings/src/main/res/values-ca/strings.xml index 1f2cf1983c..153036da0f 100644 --- a/libraries/ui-strings/src/main/res/values-ca/strings.xml +++ b/libraries/ui-strings/src/main/res/values-ca/strings.xml @@ -1160,7 +1160,7 @@ Tema "Tema: " Afegeix un tema - %s perquè la gent sàpiga de que tracta la sala. + %s perquè la gent sàpiga de què tracta la sala. Tema Tema de la sala (opcional) Esperant l\'històric xifrat @@ -2808,7 +2808,7 @@ Activa l\'editor de text enriquit Rep notificacions en aquesta sessió. Notificacions - Carregant + Carregant… Pausa l\'emissió de veu Reprodueix o reprèn l\'emissió de veu Atura l\'enregistrament d\'emissió de veu @@ -2833,4 +2833,19 @@ Format de text Enrere 30 segons Avança 30 segons - + No es pot iniciar la nova l\'emissió de veu + Emissió en directe + No es pot iniciar el missatge de veu + Comprova-ho per assegurar que el teu compte és segur + Tens sessions no verificades + Històric de l\'enquesta + Emissió de veu iniciada + Citant + Responent a %s + Editant + Mostra els xats recents al menú de compartició del sistema + Activa la compartició directa + Has finalitzat una emissió de veu. + %1$s a finalitzat una emissió de veu. + Sí, atura + \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-cs/strings.xml b/libraries/ui-strings/src/main/res/values-cs/strings.xml index b1f7df9bb4..00303591b7 100644 --- a/libraries/ui-strings/src/main/res/values-cs/strings.xml +++ b/libraries/ui-strings/src/main/res/values-cs/strings.xml @@ -2980,4 +2980,13 @@ Nelze spustit hlasovou zprávu Chyba připojení - nahrávání pozastaveno Použít formát inline kódu + Přepnout blok kódu + Přepnout citaci + Zrušit odsazení + Odsazení + Zobrazit hlasování na časové ose + Nelze dešifrovat toto hlasové vysílání. + Údaje o vašem účtu jsou spravovány odděleně na adrese %1$s. + Účet + Při aktualizaci předvoleb oznámení došlo k chybě. Zkuste to prosím znovu. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-de/strings.xml b/libraries/ui-strings/src/main/res/values-de/strings.xml index 06c477b410..971344a863 100644 --- a/libraries/ui-strings/src/main/res/values-de/strings.xml +++ b/libraries/ui-strings/src/main/res/values-de/strings.xml @@ -941,7 +941,7 @@ Url: Nutzungsbedingungen Für andere auffindbar sein - Verwende Bots, Bridges, Widgets und Sticker-Pakete + Nutze Bots, Brücken, Widgets und Sticker-Pakete Identitäts-Server Verbindung zum Identitäts-Server trennen Identitäts-Server konfigurieren @@ -2783,7 +2783,7 @@ Öffne die App auf deinem anderen Gerät Die Anfrage wurde auf dem anderen Gerät abgelehnt. Die Verbindung konnte nicht in der erforderlichen Zeit hergestellt werden. - Verbindung mit diesem Gerät nicht unterstützt. + Die Verbindung mit diesem Gerät wird nicht unterstützt. Verbindung fehlgeschlagen Überprüfe dein angemeldetes Gerät. Der unten gezeigte Code sollte angezeigt werden. Bestätige, dass beide Codes übereinstimmen: Sichere Verbindung hergestellt @@ -2919,4 +2919,13 @@ Kann Sprachnachricht nicht beginnen Verbindungsfehler − Aufnahme pausiert Als Inline-Code formatieren + Einrücken + Umfrage im Verlauf anzeigen + Entschlüsseln der Sprachübertragung nicht möglich. + Zitat umschalten + Nicht einrücken + Codeblock umschalten + Konto + Deine Kontodetails werden separat verwaltet bei %1$s. + Ein Fehler ist während der Aktualisierung deiner Benachrichtigungseinstellungen aufgetreten. Bitte versuche es erneut. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-enm/strings.xml b/libraries/ui-strings/src/main/res/values-enm/strings.xml new file mode 100644 index 0000000000..a6b3daec93 --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-enm/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-et/strings.xml b/libraries/ui-strings/src/main/res/values-et/strings.xml index 0f005fe04e..8e304efbda 100644 --- a/libraries/ui-strings/src/main/res/values-et/strings.xml +++ b/libraries/ui-strings/src/main/res/values-et/strings.xml @@ -2891,7 +2891,7 @@ Sinu koduserver veel ei toeta jutulõngade loendit. Alustasime ringhäälingukõnega Selle ringhäälingukõne esitamine ei õnnestu. - Krüptimisvigade tõttu jääb osa hääli lugemata + Dekrüptimisvigade tõttu jääb osa hääli lugemata Möödunud päevas polnud ühtegi toimumas olnud küsitlust. \nVarasemate päevade vaatamiseks laadi veel küsitlusi. @@ -2911,4 +2911,13 @@ Kuna sa hetkel salvestad ringhäälingukõnet, siis häälsõnumi salvestamine või esitamine ei õnnestu. Selleks palun lõpeta ringhäälingukõne Viga võrguühenduses - salvestamine on peatatud Kasuta lõimitud koodi vormingut + Kasutajakonto + Sinu kasutajakonto andmeid hallatakse siin: %1$s. + Selle ringhäälingukõne dekrüptimine ei õnnestu. + Näita küsitlust ajajoonel + Lisa taandrida + Eemalda taandrida + Lülita tsiteerimine sisse/välja + Lülita koodiblokk sisse/välja + Sinu teavituste seadistuste muutmisel tekkis viga. Palu proovi uuesti. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-fa/strings.xml b/libraries/ui-strings/src/main/res/values-fa/strings.xml index 9b1d367dde..8e15009c81 100644 --- a/libraries/ui-strings/src/main/res/values-fa/strings.xml +++ b/libraries/ui-strings/src/main/res/values-fa/strings.xml @@ -191,7 +191,7 @@ تنظیمات گزارش اشکال در حال بارگذاری… - باشه + قبول لغو ذخیره ترک کردن @@ -2920,4 +2920,13 @@ نمی‌توان پخش صوتی را آغاز کرد خطای اتّصال - ضبط مکث شد اعمال قالب کد درون‌خط + تغییر حالت بلوک کد + تغییر حالت نقل قول + تونرفتگی + تورفتگی + دیدن نظرسنجی در خط زمانی + حساب + ناتوان در رمزگشایی این پخش صوتی. + جزییات حسابتان جداگانه در %1$s مدیریت می‌شود. + هنگام به‌روز رسانی ترجیحات آگاهیتان خطایی رخ داد. لطفاً دوباره تلاش کنید. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-fi/strings.xml b/libraries/ui-strings/src/main/res/values-fi/strings.xml index 66f333845f..96408da021 100644 --- a/libraries/ui-strings/src/main/res/values-fi/strings.xml +++ b/libraries/ui-strings/src/main/res/values-fi/strings.xml @@ -88,8 +88,8 @@ Peruutit eston %1$s. Syy: %2$s Poistit käyttäjän %1$s. Syy: %2$s Hylkäsit kutsun. Syy: %1$s - Lähdit. Syy: %1$s - %1$s lähti. Syy: %2$s + Poistuit. Syy: %1$s + %1$s poistui. Syy: %2$s Poistuit huoneesta. Syy: %1$s Liityit. Syy: %1$s %1$s liittyi. Syy: %2$s @@ -139,9 +139,9 @@ Päivitit tässä. %s päivitti täällä. Päivitit tämän huoneen. - Teit tulevista viesteistä näkyviä käyttäjälle %1$s - %1$s teki tulevista viesteistä näkyviä käyttäjälle %2$s - Teit tulevan huonehistorian näkyväksi %1$s + Teit tulevista viesteistä näkyviä seuraaville: %1$s + %1$s teki tulevista viesteistä näkyviä seuraaville: %2$s + Teit tulevan huonehistorian näkyväksi seuraaville: %1$s Lopetit puhelun. Vastasit puheluun. Lähetit tietoja puhelun valmistelemiseksi. @@ -168,7 +168,7 @@ Liityit %1$s liittyi Liityit huoneeseen - Kutsuit %1$s + Kutsuit käyttäjän %1$s Loit keskustelun %1$s loi keskustelun Loit huoneen @@ -1311,7 +1311,7 @@ QR-koodin skannaaminen vaatii luvan kameran käyttöön. Kysy varmistusta ennen puhelun aloittamista Estä vahinkopuhelut - SSL Virhe. + SSL-virhe. Takakamera Etukamera Vaihda kameraa @@ -2119,8 +2119,8 @@ Vahvistetu Suodata - Käyttämättä %1$d päivän tai pidempään - Käyttämättä %1$d päivää tai pidempään + Käyttämättä vuorokauden tai pidempään + Käyttämättä %1$d vuorokautta tai pidempään Käyttämätön Ei valmis turvallista viestintää varten @@ -2136,7 +2136,7 @@ Käyttämättä olevat istunnot Vahvista nämä istunnot tai kirjaudu niistä ulos. Vahvistamattomat istunnot - Paranna tilisi turvallisuutta seuraamalla näitä suosituksia. + Paranna tilisi turvallisuutta noudattamalla näitä suosituksia. Turvallisuussuositukset Käyttämättä %1$d+ päivän (%2$s) @@ -2164,8 +2164,8 @@ Tällä hetkellä käytössä %s. Menetelmä - Löytyi %d menetelmä. - Löytyi %d menetelmää. + Yksi menetelmä löytyi. + %d menetelmää löytyi. Saatavilla olevat menetelmät Ilmoitusmenetelmä @@ -2357,4 +2357,76 @@ Anna kameran käyttöoikeus järjestelmän asetuksista tämän toiminnon suorittamiseksi. Tämän toiminnon suorittaminen vaatii enemmän oikeuksia. Anna oikeudet järjestelmän asetuksista. Kuunnellaan ilmoituksia + Lukemattomat viestisi näkyvät tässä sitten kun saat niitä. + Ei ilmoitettavaa. + Kirjaudu QR-koodilla + Huomaa, että istuntojen nimet näkyvät ihmisille, joiden kanssa viestit. + Istunnon nimi + IP-osoite + Käyttöjärjestelmä + Malli + Selain + URL-osoite + Versio + Nimi + Istunnon nimi + Kirjaudu ulos tästä istunnosta + Piilota IP-osoite + Näytä IP-osoite + Kirjaudu ulos kaikista muista istunnoista + + Kirjaudu ulos yhdestä istunnosta + Kirjaudu ulos %1$d istunnosta + + Kirjaudu ulos + + Harkitse vanhoista (yksi vuorokausi tai vanhemmista), käyttämättömistä istunnoista uloskirjautumista. + Harkitse vanhoista (%1$d vuorokautta tai vanhemmista), käyttämättömistä istunnoista uloskirjautumista. + + + Harkitse vanhoista (yksi vuorokausi tai vanhemmista), käyttämättömistä istunnoista uloskirjautumista. + Harkitse vanhoista (%1$d vuorokautta tai vanhemmista), käyttämättömistä istunnoista uloskirjautumista. + + Tämä istunto on valmiina turvalliseen viestintään. + Nykyinen istuntosi on valmina turvalliseen viestintään. + Kamera + Sijainti + Kyselyt + Liitteet + Tarrat + Karttaa ei voida ladata +\nTätä kotipalvelinta ei välttämättä ole säädetty näyttämään karttoja. + Virhe kyselyjä noudettaessa. + Lataa lisää kyselyjä + + Viime vuorokaudelta ei ole menneitä kyselyjä. +\nLataa lisää kyselyjä nähdäksesi aiempien päivien kyselyt. + Viimeisiltä %1$d vuorokaudelta ei ole menneitä kyselyjä. +\nLataa lisää kyselyjä nähdäksesi aiempien päivien kyselyt. + + Tässä huoneessa ei ole menneitä kyselyjä + Menneet kyselyt + + Ei kyselyjä viimeisen vuorokauden ajalta. +\nLataa lisää kyselyjä nähdäksesi aiempien päivien kyselyt. + Ei kyselyjä viimeisen %1$d vuorokauden ajalta. +\nLataa lisää kyselyjä nähdäksesi aiempien päivien kyselyt. + + Tässä huoneessa ei ole aktiivisia kyselyjä + Aktiiviset kyselyt + Salauksen purkamisvirheistä johtuen joitakin ääniä ei ehkä lasketa + Tämä estää ihmisiä äänestämästä ja näyttää kyselyn lopulliset tulokset. + Ota LaTeX-matematiikka käyttöön + %1$s jäljellä + Äänitä pitämällä painettuna, lähetä päästämällä + Huoneen päivittäminen on edistynyt toiminto, jota yleensä suositellaan, kun huone on epävakaa virheistä, puuttuvista ominaisuuksita tai tietoturvahaavoittuvuuksista johtuen. +\nSe vaikuttaa yleensä vain siihen, miten huonetta käsitellään palvelimella. + Merkitse ei-ehdotetuksi + Merkitse ehdotetuksi + Viestien lähettäminen epäonnistui + Varattu + Virhe puhelua siirrettäessä + Yhdistä + Kumoa kutsu + Kotipalvelin ei hyväksy pelkistä numeroista koostuvaa käyttäjänimeä. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-fr-rCA/strings.xml b/libraries/ui-strings/src/main/res/values-fr-rCA/strings.xml index cd39fa3381..9a9a288b99 100644 --- a/libraries/ui-strings/src/main/res/values-fr-rCA/strings.xml +++ b/libraries/ui-strings/src/main/res/values-fr-rCA/strings.xml @@ -296,7 +296,7 @@ code QR Ajouter avec un code QR Lien copié dans le presse-papiers - Ajouter un onglet dédié aux notifications non-lues sur l’écran principal. + Ajouter un onglet dédié aux notifications non lues sur l’écran principal. Activer le balayage pour répondre dans l’historique Nom ou identifiant (#exemple:matrix.org) Voir le répertoire des salons @@ -842,7 +842,7 @@ Le code est requis à l’ouverture d’${app_name}. Le code est demandé après 2 minutes d\'inutilisation d’${app_name}. Demander le code après 2 minutes - Afficher uniquement le numéro de messages non-lus dans une simple notification. + Afficher uniquement le numéro de messages non lus dans une simple notification. Afficher les détails comme les noms des salons et le contenu du message. Afficher le contenu dans les notifications Le code est la seule façon de déverrouiller ${app_name}. @@ -2068,4 +2068,4 @@ \n \nCette action va redémarrer l’application et pourra prendre du temps. Requête de synchronisation initiale - + \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-fr/strings.xml b/libraries/ui-strings/src/main/res/values-fr/strings.xml index 491a2660c4..3d08c58820 100644 --- a/libraries/ui-strings/src/main/res/values-fr/strings.xml +++ b/libraries/ui-strings/src/main/res/values-fr/strings.xml @@ -274,9 +274,9 @@ Rejoindre Rejeter Quitter le salon - Conversations privées + Messages directs Inviter - Bannir + Interdire l’accès au salon (définitif) Révoquer le bannissement Mentionner Faire confiance @@ -473,7 +473,7 @@ Chargement… Voulez-vous vraiment engager un nouvel appel audio \? Voulez-vous vraiment engager un nouvel appel vidéo \? - Bannir un utilisateur va l’expulser du salon et l’empêcher de le rejoindre à nouveau. + L’exclusion de l’utilisateur va l’expulser du salon et l’empêcher de le rejoindre à nouveau. Notification pour chaque message Ajouter à l’écran d’accueil Aperçu des liens @@ -579,7 +579,7 @@ réduire %1$s : %2$s +%d - Expulser + Retirer du salon (réversible) Afficher un aperçu des liens dans la discussion quand votre serveur d’accueil le permet. Envoyer des notifications de saisie Les autres utilisateurs pourront voir que vous êtes en train d’écrire. @@ -836,7 +836,7 @@ Changer de réseau Veuillez patienter… On dirait que vous avez déjà configuré une sauvegarde de clé depuis une autre session. Voulez-vous la remplacer par celle que vous êtes en train de créer \? - Vos conversations privées seront affichées ici. Appuyez sur « + » en bas à droite pour en démarrer une. + Vos messages directs seront affichés ici. Appuyez sur « + » en bas à droite pour en démarrer une. Salons Vos salons seront affichés ici. Appuyez sur le « + » en bas à droite pour trouver ceux existant ou en créer de nouveaux. Réactions @@ -852,7 +852,7 @@ Changer Impossible d’avoir un aperçu de ce salon Salons - Conversations privées + Messages directs CRÉER Nom Public @@ -884,7 +884,7 @@ Merci, la suggestion a bien été envoyée Échec d’envoi de la suggestion (%s) Afficher les évènements cachés dans les discussions - Conversations privées + Messages directs Attente… Chiffrement de la miniature… Envoi de la miniature (%1$s / %2$s) @@ -1410,7 +1410,7 @@ Le lien %1$s vous emmène sur un autre site : %2$s. \n \nVoulez-vous vraiment poursuivre \? - Nous n’avons pas pu créer votre conversation privée. Vérifiez les utilisateurs que vous souhaitez inviter et réessayez. + Nous n’avons pas pu créer votre message direct. Vérifiez les utilisateurs que vous souhaitez inviter et réessayez. Non chiffré Chiffré par un appareil non vérifié Vérifiez la nouvelle connexion accédant à votre compte : %1$s @@ -1570,7 +1570,7 @@ Le code est requis à l’ouverture de ${app_name}. Le code est demandé après 2 minutes d\'inutilisation de ${app_name}. Demander le code après 2 minutes - Afficher uniquement le numéro de messages non-lus dans une simple notification. + Afficher uniquement le numéro de messages non lus dans une simple notification. Afficher les détails comme les noms des salons et le contenu du message. Afficher le contenu dans les notifications Le code est la seule façon de déverrouiller ${app_name}. @@ -1607,7 +1607,7 @@ %1$s, %2$s et %3$d autre ont lu %1$s, %2$s et %3$d autres ont lu - Ajouter un onglet dédié aux notifications non-lues sur l’écran principal. + Ajouter un onglet dédié aux notifications non lues sur l’écran principal. Le salon a été créé, mais certaines invitations n’ont pas été envoyées pour la raison suivante : \n \n%s @@ -1716,12 +1716,12 @@ Sujet Sujet du salon (facultatif) Nom du salon - Conversation privée + Message direct Inclure l’historique d’échange de clés Plus aucun résultat Exporter le rapport d’audit %s pour permettre aux gens de connaître le sujet de ce salon. - Ceci est le début de l’historique de votre conversation privée avec %s. + Ceci est le début de l’historique de votre message direct avec %s. Ceci est le début de cette conversation. Ceci est le début de %s. Vous n\'avez pas le droit d’activer le chiffrement dans ce salon. @@ -2131,8 +2131,8 @@ Les invitations à des salons Les conversations de groupe chiffrées Les conversations de groupe - Les conversations privées chiffrées - Les conversations privées + Les conversations à deux chiffrées + Les messages directs Mon nom d’utilisateur Mon nom d’affichage Me notifier pour @@ -2614,7 +2614,7 @@ Préférences de présentation Parcourir les salons Créer un salon - Commencer une discussion + Commencer un message direct Non vérifiée · Dernière activité %1$s Vérifié · Dernière activité %1$s Tout voir (%1$d) @@ -2636,7 +2636,7 @@ Accéder aux espaces Pour simplifier Element, les onglets sont désormais facultatifs. Gérez les depuis le menu en haut à droite. Bienvenu dans une nouvelle vue ! - C\'est ici que vos messages non-lus s’afficheront lorsque vous en aurez. + C\'est ici que vos messages non lus s’afficheront lorsque vous en aurez. Rien à signaler. La messagerie sécurisée tout-en-un pour les équipes, les amis, et les organisations. Créez une discussion ou rejoignez un salon pour démarrer. Bienvenue dans ${app_name}, @@ -2920,4 +2920,13 @@ Impossible de démarrer un message vocal Erreur de connexion – Enregistrement en pause Appliquer le formatage de code en ligne + Bloc de code + Citation + Désindenter + Indenter + Consulter la chronologie des sondages + Impossible de déchiffrer cette diffusion audio. + Les détails de votre compte sont gérés séparément sur %1$s. + Compte + Nous avons rencontré une erreur lors de la mise-à-jour de vos préférences de notification. Veuillez réessayer. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-hu/strings.xml b/libraries/ui-strings/src/main/res/values-hu/strings.xml index a44bc9b78b..d536f075cb 100644 --- a/libraries/ui-strings/src/main/res/values-hu/strings.xml +++ b/libraries/ui-strings/src/main/res/values-hu/strings.xml @@ -2920,4 +2920,12 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze Hang üzenetet nem lehet elindítani Kapcsolódási hiba – Felvétel szüneteltetve Beágyazott kód formátum alkalmazása + Kód blokk be/ki + Idézet be/ki + Behúzás kijjebb + Behúzás + Szavazás megjelenítése az idővonalon + A hang közvetítés nem fejthető vissza. + A fiókadatok külön vannak kezelve itt: %1$s. + Fiók \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-in/strings.xml b/libraries/ui-strings/src/main/res/values-in/strings.xml index ca871db81b..3d8d1b25be 100644 --- a/libraries/ui-strings/src/main/res/values-in/strings.xml +++ b/libraries/ui-strings/src/main/res/values-in/strings.xml @@ -2862,4 +2862,13 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Anda tidak dapat memulai sebuah pesan suara karena Anda saat ini merekam sebuah siaran langsung. Silakan mengakhiri siaran langsung Anda untuk memulai merekam sebuah pesan suara Tidak dapat memulai pesan suara Terapkan format kode dalam baris + Alih balok kode + Alih kutipan + Batalkan inden + Inden + Tampilkan pemungutan suara di lini masa + Tidak dapat mendekripsi siaran suara ini. + Detail akun Anda dikelola secara terpisah di %1$s. + Akun + Terjadi kesalahan saat memperbarui preferensi notifikasi Anda. Silakan coba lagi. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-it/strings.xml b/libraries/ui-strings/src/main/res/values-it/strings.xml index 80a0b7045f..8a6db0f0e2 100644 --- a/libraries/ui-strings/src/main/res/values-it/strings.xml +++ b/libraries/ui-strings/src/main/res/values-it/strings.xml @@ -2911,4 +2911,12 @@ Impossibile iniziare il messaggio vocale Applica formato codice interlinea Errore di connessione - Registrazione in pausa + Attiva/disattiva blocco di codice + Attiva/disattiva citazione + Rimuovi indentazione + Indenta + Vedi sondaggio nella linea temporale + Impossibile decifrare questa trasmissione vocale. + I dettagli del tuo account sono gestiti separatamente su %1$s. + Account \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-ja/strings.xml b/libraries/ui-strings/src/main/res/values-ja/strings.xml index f51f2ba81a..cf28976e65 100644 --- a/libraries/ui-strings/src/main/res/values-ja/strings.xml +++ b/libraries/ui-strings/src/main/res/values-ja/strings.xml @@ -143,7 +143,7 @@ このセッションで通知を有効にする 1対1のチャットでのメッセージ グループチャットでのメッセージ - ルームへ招待されたとき + ルームに招待されたとき 通話への招待 ボットによるメッセージ 端末起動時に開始 @@ -252,7 +252,7 @@ 公開端末名 ルームのエンドツーエンド暗号鍵をエクスポート 認証済 - このルームに参加していません。 + あなたはこのルームのメンバーではありません。 このルームでそれを行う権限がありません。 ルーム %s は閲覧できません。 ユーザー名 @@ -394,7 +394,7 @@ 指定したIDのユーザーをブロック 指定したIDのユーザーのブロックを解除 ユーザーの権限レベルを規定 - 指定したIDのユーザーの管理者権限を取り消す + 指定したIDのユーザーの権限をリセット 指定したIDのユーザーを現在のルームに招待 指定したアドレスのルームに参加 ルームから退出 @@ -406,7 +406,7 @@ %1$sのホームサーバーを引き続き使用するには、利用規約を確認して同意する必要があります。 エラー 確認 - アカウントを無効化 + アカウントの無効化 この操作により、あなたのアカウントは永久に使えなくなります。ログインしたり同じユーザーIDを再登録したりすることはできなくなります。あなたのアカウントは参加している全てのルームから退出し、あなたのIDサーバーからアカウントの詳細が削除されます。<b>この操作は取り消せません。</b> \n \nアカウントを無効化しても、<b>デフォルトではあなたが送信したメッセージの履歴は消去されません</b>。メッセージの履歴を消去する場合は、以下のボックスにチェックを入れてください。 @@ -418,7 +418,7 @@ このルームは置き換えられており、アクティブではありません。 こちらから継続中の会話を確認 このルームは別の会話の続きです - 以前のメッセージを表示するには、ここをクリックしてください + ここをクリックすると、以前のメッセージを表示します サービス管理者に連絡 このホームサーバーはリソース制限の1つを超過しているため、 ユーザーがログインできなくなることがあります このホームサーバーはリソースの上限に達しました。 @@ -484,7 +484,7 @@ 通知に関する問題解決 システムの設定。 アカウントの設定。 - カスタム設定。 + ユーザー定義の設定。 端末起動時に開始 バックグラウンド制限の確認 編集 @@ -537,13 +537,13 @@ ルームから退出しています… 管理者 モデレーター - カスタム + ユーザー定義 招待中 ユーザー %1$sの管理者 %1$sのモデレーター %1$sの既定のユーザー - %2$sのカスタム(%1$d) + %2$sのユーザー定義(%1$d) タイムライン エンドツーエンド暗号化を有効にする… 暗号化を有効にする @@ -572,7 +572,7 @@ アカウントデータ 削除… 削除の確認 - このイベントを削除してよろしいですか?ルーム名やトピックの変更を削除すると、変更が取り消されます。 + このイベントを削除してよろしいですか?ルームの名前やトピックの変更を削除すると、変更が取り消される可能性があります。 暗号化が有効です このルーム内でのメッセージはエンドツーエンドで暗号化されます。詳細の確認や認証はユーザーのプロフィールをご確認ください。 暗号化が有効になっていません @@ -714,7 +714,7 @@ Playサービスを修正 Google PlayサービスのAPKは利用可能で最新の状態になっています。 Playサービスのチェック - 一部の通知はカスタム設定で無効になっています。 + 一部の通知はユーザー定義の設定で無効になっています。 一部のメッセージがサイレントに設定されていることに注意してください(音を出さずに通知します)。 1つ以上のテストが失敗しました。調査用のバグレポートを送信してください。 1つ以上のテストが失敗しました。提案された修正を試してください。 @@ -911,8 +911,8 @@ %1$s、%2$s、%3$sと%4$s %1$s、%2$sと%3$s %1$sの権限レベルを%2$sから%3$sへ - カスタム - カスタム (%1$d) + ユーザー定義 + ユーザー定義 (%1$d) 既定 モデレーター 管理者 @@ -940,7 +940,7 @@ これにより、現在のキーまたはフレーズが置き換えられます。 新しいセキュリティーキーを生成するか、既存のバックアップに新しいセキュリティーフレーズを設定してください。 サーバー上の暗号鍵をバックアップして、暗号化されたメッセージとデータへのアクセスが失われるのを防ぎましょう。 - メッセージ作成画面に絵文字キーボードを開くためのボタンを追加 + メッセージ入力欄に絵文字キーボードを開くためのボタンを追加 絵文字キーボードを表示 アバターと表示名の変更を含む。 アカウントのイベントを表示 @@ -1076,7 +1076,7 @@ バックアップには認証済のセッション %s による署名があります。 バックアップにはこのセッションによる有効な署名があります。 バックアップには%sというIDの不明のセッションによる署名があります。 - このセッションでは鍵がバックアップされていません。 + 鍵はこのセッションからバックアップされていません。 このセッションでは鍵のバックアップが無効になっています。 このセッションでは鍵のバックアップが正しく設定されています。 最新の復号化キーのバージョンを取得するのに失敗しました(%s)。 @@ -1144,7 +1144,7 @@ \n直近のオンライン日時:%2$s \n新しいセッションにログインしなかった場合、この要求を無視してください。 鍵の共有リクエスト - カスタムカメラ画面の代わりにシステムカメラを開始。 + ユーザー定義のカメラ画面の代わりにシステムカメラを開始。 使用中のウィジェットがありません インテグレーションを管理 DRMで保護されているメディアを読み込む @@ -1187,7 +1187,7 @@ スペースのメンバーのみ 誰でもルームを発見し参加できます 公開 - 招待した人のみが検索・参加できます + 招待した人のみが検索し、参加できます 非公開 不明のアクセス設定(%s) 誰でもルームにノックができ、メンバーがその参加を承認または拒否できます @@ -1197,7 +1197,7 @@ このアドレスを公開 アドレスを設定すると、他のユーザーがあなたのホームサーバー(%1$s)を通じてこのルームを見つけられるようになります。 ローカルアドレス - 新しい公開アドレス(例:#alias:server) + 新しい公開アドレス(例:#alias:server) 他の公開アドレスはまだありません。以下から追加できます。 他の公開アドレスはまだありません。 アドレス\"%1$s\"を非公開にしますか? @@ -1252,7 +1252,7 @@ Matrix IDでサインイン 詳細を表示 その他 - カスタムと高度な設定 + ユーザー定義と高度な設定 組織向けのプレミアムホスティング 組織向けのプレミアムホスティング 最大の公開サーバーで、数百万人に無料で参加 @@ -1333,8 +1333,8 @@ これを使用するとデータが%sと共有される可能性があります: %1$s:%2$s %3$s %1$s:%2$s - あなたが知らないかもしれない他のスペースやルーム - 誰がこのルームを検索・参加できるか選択してください。 + 知らないかもしれない他のスペースやルーム + 誰がこのルームを検索し、参加できるか選択してください。 タップしスペースを編集 スペースを選択 アクセス可能なスペース @@ -1354,8 +1354,8 @@ ダイレクトメッセージ 自分のユーザー名 自分の表示名 - グループチャットで暗号化されたメッセージ - 1対1のチャットで暗号化されたメッセージ + グループチャットでの暗号化されたメッセージ + 1対1のチャットでの暗号化されたメッセージ 以下の場合に通知 その他 メンションとキーワード @@ -1436,8 +1436,8 @@ %1$s個以上の選択肢が必要です - 呼び出したユーザーは話し中です。 - 話し中です + 呼び出したユーザーは通話中です。 + 通話中 %sとの音声通話 %sとのビデオ通話 呼び出しています… @@ -1507,7 +1507,7 @@ 警告! 位置情報 メディア - アンケート終了 + アンケートが終了しました アンケートを終了しますか? アンケートを削除 スペースを作成 @@ -1628,11 +1628,11 @@ 不適切として報告済 添付ファイルを送信 詳細なログを有効にする。 - メールアドレスか電話番号でアカウントを見つけてもらえるようにするには、IDサーバー(%s)の利用規約への同意が必要です。 + メールアドレスか電話番号でアカウントを検出可能にするには、IDサーバー(%s)の利用規約への同意が必要です。 ${app_name}は位置情報にアクセスできませんでした ${app_name}は位置情報にアクセスできませんでした。後でもう一度やり直してください。 音声メッセージ(%1$s) - 推奨のルームバージョンへとアップグレード + 推奨のルームバージョンにアップグレード 音声メッセージを録音 あなたのホームサーバーの運営方針 一番下に移動 @@ -1707,7 +1707,7 @@ 素早くクラッシュ 開発者モードは隠された機能を有効にするため、アプリケーションが不安定になる恐れがあります。開発者向けです! 復号エラーが生じた際に、自動的にログを送信 - 復号エラーを自動的に報告する。 + 復号エラーを自動で報告。 変更を有効にするにはアプリケーションの再起動が必要です。 LaTeXによる数学表記を有効にする 以下の場合に通知 @@ -1731,7 +1731,7 @@ 投票する 投票した人には、投票の際に即座に結果が表示されます アンケートの終了後に結果を公開 - 結果はアンケートを終了した後でのみ明らかにされます + 結果はアンケートが終了した後で表示されます 以下で開く 暗号化のアップグレードが利用できます SSSSキーをリカバリーキーから生成しています @@ -1812,7 +1812,7 @@ サーバーのバージョン サーバー名 電話番号が正しくないようです。確認してください - カスタムホームサーバーを選択 + ユーザー定義のホームサーバーを選択 Element Matrix Servicesを選択 再送信 コードを入力 @@ -1825,7 +1825,7 @@ 信頼済 認証済 未送信のメッセージを削除 - カスタムイベントを送信 + ユーザー定義のイベントを送信 ルームの状態を調査 開封確認メッセージを表示 通知しない @@ -1842,7 +1842,7 @@ 合計%1$d票。投票すると結果を確認できます - 未認証の端末で暗号化 + 未認証の端末による暗号化 メッセージを紙吹雪と共に送信 メッセージを降雪と共に送信 紙吹雪🎉を送る @@ -1863,7 +1863,7 @@ 正しい参加者が%sにアクセスできるようにしましょう。 連絡先を追加 - %d人の知り合いがすでに参加しています + %d人の知人が既に参加しています %sに招待 ユーザー名かメールアドレスで招待 @@ -1874,18 +1874,18 @@ 注意:アプリケーションが再起動します ホームサーバーの管理者にお問い合わせください あなたが参加している全てのルームがホームに表示されます。 - 親のスペースを自動的に更新 + 上位のスペースを自動的に更新 残り%1$d秒 %sに属する人は、誰でもこのルームを検索し、参加することができます。手動で全員を招待する必要はありません。これはルームの設定からいつでも変更できます。 - 親のスペースに属する人は、誰でもこのルームを検索し、参加することができます。手動で全員を招待する必要はありません。これはルームの設定からいつでも変更できます。 + 上位のスペースに属する人は、誰でもこのルームを検索し、参加することができます。手動で全員を招待する必要はありません。これはルームの設定からいつでも変更できます。 %1$d個の投票に基づく - 合計%1$d票の投票に基づく最終結果 + 合計%1$d票に基づく最終結果 新しいセッションが認証されました。セッションは暗号化されたメッセージにアクセスすることができます。また、セッションは他のユーザーに「信頼済」として表示されます。 - このルームを同じホームサーバー上で組織内のチームとのコラボレーションにのみ使用する場合、このオプションを有効にするといいかもしれません。これは後から変更できません。 + このルームを、あなたのホームサーバーで、組織内のチームとのコラボレーションにのみ使用するなら、この設定を有効にするといいかもしれません。これは後から変更できません。 %sに属していないユーザーによるこのルームへの参加を、今後永久に拒否 プレーンテキストメッセージの前に ( ͡° ͜ʖ ͡°) を付ける このメールアドレスのドメインの登録は許可されていません @@ -1906,7 +1906,7 @@ これは正しいユーザーIDではありません。正しいフォーマットは「@user:homeserver.org」です。 パスワードを忘れた場合、戻ってパスワードを再設定してください。 メールボックスを確認してください - カスタムサーバーに接続 + ユーザー定義のサーバーに接続 既にアカウントがあります 既存のサーバーに参加しますか? この質問をスキップ @@ -1991,7 +1991,7 @@ このルームはまだ作成されていません。キャンセルしますか? テキストメッセージで共有 保護を設定 - このルームのみ + このルームにのみ 誰でも参加可能。コミュニティー向け 既存のスペースに参加するには、招待が必要です。 これは後から変更できます @@ -2033,9 +2033,9 @@ \n%sで利用規約を閲覧できます。 最初の検索結果のみ表示しています。文字をもっと入力してください… matrix.toリンクのフォーマットが正しくありませんでした - 注意!この端末には暗号鍵を含む個人データが保存されています。 + 警告:あなたの個人データ(暗号化の鍵を含む)が、この端末に保存されています。 \n -\nこの端末での使用を終了、または他のアカウントにサインインする場合、このデータをクリアしてください。 +\nこの端末の使用を終了するか、他のアカウントにログインしたい場合は、そのデータを消去してください。 この端末に現在保存されている全てのデータをクリアしますか? \nアカウントデータとメッセージにアクセスするにはもう一度サインインしてください。 現在のセッションはユーザー %1$s のものですが、あなたが提供している認証情報はユーザー %2$s のものです。この操作は${app_name}ではサポートされていません。 @@ -2188,10 +2188,10 @@ ディスカバリーの設定を終了します。 ここの参加者はあなただけです。退出すると、今後あなたを含めて誰も参加できなくなります。 再び招待されない限り、再参加することはできません。 - あなたはこのスペースの唯一の管理者です。退出すると、誰もそれをコントロールすることができなくなります。 + あなたはこのスペースの唯一の管理者です。退出すると、誰もそれを管理できなくなります。 名前のないルーム - このルームはホームサーバーが不安定と判断したルームバージョン%sで動作しています。 - 申し訳ありませんが、%sに参加する途中で問題が発生しました + このルームは、ホームサーバーが不安定と判断したルームバージョン%sで動作しています。 + 申し訳ありませんが、%sに参加する際に問題が発生しました このルームへの招待が、アカウントに関連付けられていないメールアドレス %s に送られました 投票がありません ルーム全体に通知 @@ -2238,7 +2238,7 @@ アプリの名前を変更しました!アプリは最新版で、アカウントにはログイン済です。 ステートイベントを送信 ステートイベント - カスタムのステートイベントを送信 + ユーザー定義のステートイベントを送信 ステートイベントを送信しました! 続行するには名前を設定してください。 どんな作業に取り組みますか? @@ -2318,7 +2318,7 @@ 鍵のバックアップの機密情報をSSSSに保存しています あなたしか知らないセキュリティーフレーズを入力してください。サーバーで機密情報を保護するために使用します。 詳細を非表示 - 他の参加者はいません。%sに招待しましょう。 + まだ他の参加者はいません。%sに招待しましょう。 位置情報(ライブ)が有効です 現在の位置情報を共有 現在の位置情報を共有 @@ -2503,11 +2503,11 @@ %1$d日以上使用されていません(%2$s) 地図を読み込めません -\nこのホームサーバーは地図が読み込むよう設定されていないおそれがあります。 +\nこのホームサーバーは地図が読み込めるように設定されていないおそれがあります。 スペースは、ルームや連絡先をまとめる新しい方法です。右下のボタンを使うと、既存のルームを追加したり新たに作成したりできます。 セキュリティーに関する勧告 その他のセッション - セキュリティーを最大限に高めるには、不明なセッションや利用していないセッションからサインアウトしてください。 + セキュリティーを最大限に高めるには、不明なセッションや使用していないセッションからサインアウトしてください。 生体認証を有効にできませんでした。 関連付けに失敗しました。 おかえりなさい! @@ -2526,7 +2526,7 @@ 未読のメッセージがある場合は、ここに表示されます。 未読はありません。 クライアントの情報の保存を有効にする - セッション名は連絡先にも表示されます。 + セッション名は連絡先に対しても表示されます。ご注意ください。 セッション名を設定すると、端末をより簡単に認識できるようになります。 このセッションでプッシュ通知を受信。 絞り込みを解除 @@ -2541,7 +2541,7 @@ 未認証のセッションはありません。 認証済のセッションはありません。 - 使用していない古いセッション(%1$d日以上使用されていません)からサインアウトすることを検討してください。 + 使用していない古いセッション(%1$d日以上使用されていません)からのサインアウトを検討してください。 非アクティブ セッションを認証すると、より安全なメッセージのやりとりが可能になります。見覚えのない、または使用していないセッションがあれば、サインアウトしましょう。 @@ -2614,7 +2614,7 @@ Googleサービス 通知の受信方法を選択してください 画面を共有しています - ${app_name}画面共有 + ${app_name}の画面共有 テキストの装飾 連絡先 カメラ @@ -2631,14 +2631,14 @@ 一時的な実装。位置情報がルームの履歴に残ります 位置情報(ライブ)の共有を有効にする 位置情報を共有しています - ${app_name}位置情報(ライブ) + ${app_name}の位置情報(ライブ) 残り%1$s 位置情報(ライブ)を表示 位置情報(ライブ)が終了しました 位置情報(ライブ)を読み込んでいます… 8時間 1時間 - 15分 + 15分間 位置情報(ライブ)を共有する時間 現在の位置にズーム 地図で選択した位置情報のピン @@ -2656,7 +2656,7 @@ ライブ配信を終了してよろしいですか?配信を終了し、録音をこのルームで利用できるよう設定します。 ライブ配信を停止しますか? 残り%1$s - 接続エラー - 録音を停止しました + 接続エラー - 録音を一時停止しました この音声配信を再生できません。 既に音声配信を録音しています。新しく始めるには今の音声配信を終了してください。 他の人が既に音声配信を録音しています。新しく始めるには音声配信が終わるまで待機してください。 @@ -2800,7 +2800,7 @@ %s \nは空です。 クライアントの名称、バージョン、URLを記録し、セッションマネージャーでより容易にセッションを認識できるよう設定。 - セッション名を変更 + セッション名の変更 絞り込む 直近のオンライン日時 %1$s @@ -2811,7 +2811,7 @@ 未認証・直近のオンライン日時 %1$s 認証済・直近のオンライン日時 %1$s 現在のセッションを認証すると、このセッションの認証の状態を確認できます。 - セキュリティーを最大限に高めるには、セッションを認証し、不明なセッションや利用していないセッションからサインアウトしてください。 + セキュリティーを最大限に高めるには、セッションを認証し、不明なセッションや使用していないセッションからサインアウトしてください。 Element Callウィジェットを自動で承認し、カメラまたはマイクのアクセス権を付与 Element Callの権限のショートカットを有効にする 現在のゲートウェイ:%s @@ -2854,4 +2854,18 @@ 認証済のセッションは、パスフレーズの入力、または他の認証済のセッションで本人確認を行ったセッションです。 \n \n認証済のセッションには、暗号化されたメッセージを復号化する際に使用する全ての鍵が備わっています。また、他のユーザーに対しては、あなたがこのセッションを信頼していることが表示されます。 + %1$s タップして戻る + もう一方の端末でサインインしてください。 + 携帯端末にサインインしますか? + サインインの画面で開始 + サインインの画面で開始 + セキュアなメッセージのやり取りを設定している際に、セキュリティー上の問題に遭遇しました。あなたのホームサーバー、インターネット接続、端末のいずれかの安全性が損なわれている可能性があります。 + コードブロックの表示を切り替える + あなたのアカウントの詳細は%1$sで管理されています。 + 引用の表示を切り替える + インデントを減らす + インデントを増やす + アンケートをタイムラインに表示 + この音声配信を復号化できません。 + アカウント \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-lv/strings.xml b/libraries/ui-strings/src/main/res/values-lv/strings.xml index 9201bf146a..81a7a10dcc 100644 --- a/libraries/ui-strings/src/main/res/values-lv/strings.xml +++ b/libraries/ui-strings/src/main/res/values-lv/strings.xml @@ -39,7 +39,6 @@ Uzaicinājums uz istabu %1$s un %2$s Tukša istaba - Jūs nomainījāt savu parādāmo vārdu no %1$s uz %2$s Jūs nomainījāt savu parādāmo vārdu uz %1$s Jūs nomainījāt savu avataru @@ -222,7 +221,6 @@ Dzēst Pārdēvēt Ziņot par saturu - vai Uzaicināt Izrakstīties @@ -245,7 +243,6 @@ Vienīgi Matrix kontakti Nav rezultātu Istabas - Nosūtīt logfailus Nosūtīt sistēmas avārijas logfailus Nosūtīt ekrānattēlu @@ -272,7 +269,7 @@ Gaiša tēma Tumša tēma Melna tēma - Notikumu monitorings + Uztver notikumus Skaņas paziņojumi Klusi paziņojumi Kļūdas atskaite @@ -285,10 +282,8 @@ Šķiet ievadīta nederīga epasta adrese Šī epasta adrese jau tiek izmantota. Aizmirsāt paroli\? - Mājasservers vēlas pārbaudīt, vai neesat robots Neizdevās verificēt epasta adresi: pārbaudiet, vai esi noklikšķinājis(usi) uz saiti atsūtītajā epastā - Ievadi korektu URL adresi Bojāts JSON Nav derīgs JSON @@ -305,15 +300,10 @@ Notiek zvans… Adresāts neatbildēja uz zvanu. Element informācija - - ${app_name}-am nepieciešama atļauja piekļūt mikrofonam, lai nodrošinātu audio zvanus. - ${app_name} nepieciešama atļauja piekļūt kamerai un mikrofonam, lai veiktu videozvanus. \n \nLūdzu, dodiet piekļuves atļauju nākamajā uznirstošajā logā, lai būtu iespēja veikt zvanus. - - Turpināt @@ -321,7 +311,6 @@ Pievienoties Noraidīt Pāriet uz pirmo neizlasīto ziņu - Pamest istabu Vai tiešām vēlies pamest istabu\? TIEŠIE ČATI @@ -349,7 +338,6 @@ Servera sertifikāts ir izmanījies un Tava ierīce tam tagad neuzticas. Tas ir ĻOTI NEPARASTI. Iesakām NEUZTICĒTIES šim jaunajam sertifikātam. Sertifikāts izmainījās no iepriekš uzticama uz neuzticamu. Iespējams, serveris ir aktualizējis savu sertifikātu. Sazinies ar servera administratoru, lai saņemtu sagaidāmo nospiedumu (fingerprint). Akceptē sertifikātu TIKAI tad, kad servera administrators publicējis sertifikāta nospiedumu, kurš atbilst augstāk redzamajam. - Meklēšana Istabas dalībnieku filtrs Rezultātu nav @@ -407,7 +395,6 @@ Ierīces nosaukums Pēdējo reizi manīts %1$s @ %2$s - Autentifikācija Pierakstījies kā Mājasserveris @@ -450,7 +437,6 @@ %d biedru izmaiņas Biedru katalogs - %d biedri %d biedrs @@ -461,11 +447,8 @@ %d jauna ziņa %d jaunas ziņas - - Iestatīt kā galveno adresi Atiestatīt kā galveno adresi - Tēma Atšifrēšanas kļūda Ierīces nosaukums @@ -475,9 +458,8 @@ Eksportēt istabas atslēgas Eksportēt atslēgas vietējā failā Eksportēt - Ievadiet frāzveida paroli - Apstiprināt frāzveida paroli - + Ievadīt paroles vārdkopu + Apstiprināt paroles vārdkopu Importēt E2E istabas atslēgas Importēt istabas atslēgas Importēt atslēgas no vietējā faila @@ -490,17 +472,15 @@ Apstipriniet, salīdzinot sekojošo ar lietotāja iestatījumiem citā savā sesijā: Ja tā sakrīt, nospied zemāk esošo verifikācijas pogu. Ja tā nesakrīt, tad kāds ir pārtvēris šo ierīci un Tu droši vien vēlies šo ierīci pievienot melnajam sarakstam. Nākotnē šī pārbaudes procedūra plānota sarežģītāka. - Izvēlies istabu katalogu Mājasservera nosaukums Visas istabas %s serverī Visas vietējās %s istabas - %d nelasīta paziņota ziņa - %d nelasītas paziņotas ziņas - %d nelasītu paziņotu ziņu + %d nelasītu paziņotu ziņu + %d nelasīta paziņota ziņa + %d nelasītas paziņotas ziņu - %d istaba %d istabas @@ -908,8 +888,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Nosūtīja jums uzaicinājumu %s vēlas verificēt jūsu sesiju Verifikācijas pieprasījums - - Nodrošinieties pret piekļuves zaudēšanu šifrētām ziņām un datiem Neparedzēta kļūda Izveidot frāzveida paroli @@ -936,7 +914,7 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Iztrūks obligāts parametrs. %1$s: %2$s %3$s %1$s: %2$s - ** Neizdevās nosūtīt - atveriet istabu + ** Neizdevās nosūtīt - lūgums atvērt istabu Es Jauns uzaicinājums Jaunas ziņas @@ -1036,7 +1014,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. %1$d cilvēki %1$d cilvēki - Gaida %s… Verificēts %s Verificē %s @@ -1159,7 +1136,7 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Jūs neizmantojat nevienu identitāšu serveri Rezerves kopiju nevarēja atšifrēt ar šo atkopšanās atslēgu: lūdzu, pārbaudiet, vai ievadījāt pareizo atkopšanās atslēgu. Kalkulē atkopšanās atslēgu… - Frāzveida parole pārāk vāja + Paroles vārdkopa ir pārāk vāja Nosūta doto ziņu ar sniegu Nosūta doto ziņu ar konfeti Atbastīta tikai šifrētās istabās @@ -1247,8 +1224,8 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Gandrīz galā! Vai %s redzams tas pats vairogs\? Kamēr šis lietotājs nav padarījis šo sesiju uzticamu, ziņas uz un no tās ir marķētas ar brīdinājumiem. Alternatīvi, jūs varat manuāli verificēt šos sesiju. %1$s (%2$s) pierakstījās, izmantojot jaunu sesiju: - Lūdzu, ievadiet frāzveida paroli - Frāzveida paroles nesakrīt + Lūgums ievadīt paroles vārdkopu + Paroles vārdkopa nesakrīt Piekļuve istabai Pārvaldiet epasta adreses un tālruņu numurus, kas saistīti ar jūsu Matrix kontu Epasti un tālruņa numuri @@ -1290,7 +1267,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Jauna šifrēto ziņu atslēga Izmantot Atslēgu Dublēšanu Nekad nezaudējiet šifrētās ziņas - Dzēst savu dublēto šifrēšanas atslēgu no servera\? Jūs vairs nevarēsiet izmantot atkopšanas atsļēgu, lai lasītu šifrēto ziņu vēsturi. Izdzēst dublējumu Pārbauda dublējuma statusu @@ -1303,10 +1279,10 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Izmantojiet savu Atgūšanas Atslēgu, lai atbloķētu savu šifrēto ziņu vēsturi izmantojiet savu atgūšanas atslēgu Lietotāju ielūgšana, izmešana un aizliegšana tika neaizskarta. - Konfigurēt Skaļās Notifikācijas + Uzstādīt skaļos paziņojumus Izslēgt ierobežojumus Pārbaudīt fona ierobežojumus - Notifikācija tika nospiesta! + Paziņojumam tika piesists! Neizdevās reģistrēt FCM žetonu mājasserverī: \n%1$s FCM žetons veiksmīgi reģistrēts mājasserverī. @@ -1322,19 +1298,17 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. \nPievienojiet dažas tagad\? Neizdevās izveidot reālā laika savienojumu. \nLūdzu, jautājiet sava servera administratoram, lai konfigurētu PAGRIEZIENA serveri, lai zvani strādātu uzticami. - Izmantojiet Integrācijas Pārvaldnieku, lai pārvaldītu botus, tiltus, logrīkus un uzlīmes. \nIntegrācijas Pārvaldnieks saņem konfigurācijas datus un var modificēt logrīkus, sūtīt istabu uzaicinājumus jūsu vārdā. - - Jūs nesaņemsiet jaunas notifikācijas, kad aplikācija ir fonā. + Par ienākošajiem ziņojumiem netiks paziņots, kad lietotne darbojas fonā. Bez fona sinhronizācijas Optimizēts reālajam laikam Fona Sinhronizācijas Režīms Izvēlaites LED krāsu, vibrāciju, skaņu… - Konfigurēt Klusās Notifikācijas - Konfigurēt Zvanu Notifikācijas + Uzstādīt klusos paziņojumus + Uzstādīt zvanu paziņojumus Pakalpojums startēsies, kad ierīce būs restartēta. - Notifikāciju Displejs + Paziņojumu attēlošana Google Play Servisu APK ir pieejams un atjaunināts. Filtrēt aizliegtos lietotājus Lūdzu palaidiet ${app_name} citā ierīcē, kas var atšifrēt ziņu, lai tā varētu nosūtīt šīs sesijas atslēgas. @@ -1380,21 +1354,20 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Lūdzu uztaisiet kopiju Stop Aizvietot - Dublējums jau pastāv jūsu mājasserverī - Atgūšanas atslēgas tika saglabātas. - - Saglabāt kā Failu + Rezerves kopija jau pastāv jūsu mājasserverī + Atkopšanas atslēgas tika saglabātas. + Saglabāt datnē Saglabāt Atgūšanas Atslēgu Es uztaisīju kopiju Saglabājiet savu atgūšanas atslēgu kaut kur ļoti drošā vietā, piemēram, paroles pārvaldniekā (vai seifā) Jūsu atkopšanas atslēga ir drošības tīkls - to var izmantot, lai atjaunotu piekļuvi jūsu šifrētajām ziņām, ja esat aizmirsis savu paroles frāzi. \nSaglabājiet savu atgūšanas atslēgu kaut kur ļoti drošā vietā, piemēram, paroles pārvaldniekā (vai seifā) - Jūsu atslēgas tiek dublētas. - Izdevās ! - (Advancēti) Iestatīt ar Atgūšanas Atslēgu - Vai, aizsargājiet jūsu dublējumu ar Atgūšanas Atslēgu, saglabājot to drošā vietā. - Taisa Dublējumu - Iestatīt Frāzi + Tiek veidota atslēgu rezerves kopija. + Izdevās! + (Papildu) Uzstādīt ar atkopšanas atslēgu + Vai aizsargā rezerves kopiju ar atkopšanas atslēgu, saglabājot to drošā vietā. + Veido rezerves kopiju + Uzstādīt paroles vārdkopu Mēs saglabāsim šifrētu jūsu atslēgu kopiju savā serverī. Aizsargājiet savu dublējumu ar frāzi, lai tā būtu droši aizsargāta. \n \nLai nodrošinātu maksimālu drošību, tam jāatšķiras no jūsu konta paroles. @@ -1454,7 +1427,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Publicēt jaunu adresi manuāli Šī ir galvenā adrese ${app_name} apkopo anonīmu analītiku, lai ļautu mums uzlabot aplikāciju. - Šis aizvietos jūsu esošo Atslēgu vai Frāzi. Izveidot jaunu Drošības Atslēgu vai iestatīt jaunu Drošības Frāzi jūsu esošajam dublējumam. Iestatīt uz šīs ierīces @@ -1509,7 +1481,7 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Pārbaudes diagnostika Paziņojumu pārbaude Paziņojumu svarīgums - Advancēti Notifikāciju Iestatījumi + Paziņojumu papildu iestatījumi Jūs apturējāt zvanu %s apturēja zvanu Apturēt @@ -1657,8 +1629,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Kopīgojiet atkopšanas atslēgu ar… Lūdzu, %s, lai turpinātu izmantot šo pakalpojumu. Lūdzu, %s, lai palielinātu šo limitu. - - Palaidiet sistēmas kameru, nevis pielāgotās kameras ekrānu. Lasīt DRM aizsargātu multividi Izmantojot to, var kopīgot datus ar %s: @@ -1700,7 +1670,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. %d izvēlēti Modificēt logrīku - Trūkst atļauju Lai veiktu šo darbību, lūdzu, piešķiriet kamerai atļauju sistēmas iestatījumos. Lai veiktu šo darbību, trūkst dažu atļauju. Lūdzu, sistēmas Iestatījumos piešķiriet atļaujas. @@ -1778,7 +1747,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Savienoties %1$s Pieskarieties, lai atgrieztos Aktīvs zvans (%1$s) · - Aktīvs zvans (%1$s) Tika pieļauta kļūda, meklējot tālruņa numuru Zvanu taustiņi @@ -1833,7 +1801,7 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Neatļaut ekrānšāviņus lietotnē Izmantot failu Ievadiet savu %s, lai turpinātu - Notifikāciju iestatījumi + Paziņojumu uzstādīšana Neizdevās importēt atslēgas %s, lai ļautu cilvēkiem uzzināt, par ko ir šī istaba. Nokopējiet to uz personālās mākoņa krātuves @@ -1945,7 +1913,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. \n• Jūsu servera administrators ir anulējis jūsu piekļuvi drošības apsvērumu dēļ. Neizskatās pēc derīgas e-pasta adreses Nosūta doto ziņojumu kā pārsteigumu - %1$s pie %2$s Šajā istabā nav multivides Multivide @@ -1961,7 +1928,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Atveriet izvēlni izveidot telpu Atveriet navigācijas atvilktni Izskatās, ka serveris pārāk ilgi neatbild, to var izraisīt slikts savienojums vai servera kļūda. Lūdzu, pēc brīža mēģiniet vēlreiz. - Identitātes serveris nesniedz nekādu politiku Slēpt identitātes servera politiku Parādīt identitātes servera politiku @@ -2000,7 +1966,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Šifrētās tiešās ziņas Tiešās ziņas Mans lietotājvārds - Neizdevās iegūt jaunāko atjaunošanas atslēgu versiju (%s). %d jaunu atslēgu tika pievienotas šai sesijai. @@ -2011,7 +1976,7 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Iegūst rezerves kopiju… Atjaunošanas atslēgas ģenerēšana, izmantojot paroli, šis process var aizņemt vairākas sekundes. Izskatās, ka jums jau ir izveidots atslēgas dublējums no citas sesijas. Vai vēlaties to aizstāt ar izveidoto\? - Lūdzu, dzēsiet parolesfrāzi, ja vēlaties, lai ${app_name} ģenerētu atkopšanas atslēgu. + Lūgums izdzēst paroles vārdkopu, ja ir vēlams, lai ${app_name} izveidotu atkopšanas atslēgu. Nav atrasts derīgs Google Play Services APK. Paziņojumi var nedarboties pareizi. Marķēšana ir izslēgta. Marķēšana ir ieslēgta. @@ -2063,9 +2028,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. izmetot lietotāju, noņems viņu no šīs Telpas. \n \nLai novērstu viņu atkārtotu pievienošanos, jums tā vietā vajadzētu viņiem aizliegt pievienoties. - - - Beidz zvanu… Nav atbildes Lietotājs, kuram zvanījāt, ir aizņemts. @@ -2091,4 +2053,18 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Lai sūtītu balss ziņojumus, piešķiriet Mikrofona atļauju. Telpas Uztver paziņojumus - + Atvērt aptauju + Sniegt atgriezenisko saiti + Piesist augšējā labajā stūrī, lai redzētu iespēju sniegt atgriezenisko saiti. + Tu beidzi balss pārraidi. + %1$s izbeidza balss pārraidi. + Izmantot ierīci, kurā veikta pieteikšanās, lai nolasītu zemāk esošo kvadrātkodu: + Nolasīt zemāk esošo kvadrātkodu ar ierīci, kurā ir notikusi atteikšanās. + Pieteikties ar kvadrātkodu + Jāizmanto ierīces kamera, lai nolasītu citā ierīcē attēlotu kvadrātkodu: + Nolasīt kvadrātkodu + 3 + 2 + 1 + Izmēģināt + \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-pl/strings.xml b/libraries/ui-strings/src/main/res/values-pl/strings.xml index 2bdd6e806d..0c87b20780 100644 --- a/libraries/ui-strings/src/main/res/values-pl/strings.xml +++ b/libraries/ui-strings/src/main/res/values-pl/strings.xml @@ -2827,4 +2827,5 @@ Witaj w ${app_name}, \n%s. Wszechstronna, bezpieczna aplikacja do czatowania dla zespołów, przyjaciół i organizacji. Utwórz czat lub dołącz do istniejącego pokoju, aby rozpocząć. + Twój token dostępu zapewnia pełny dostęp do Twojego konta. Nie udostępniaj go nikomu. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-sk/strings.xml b/libraries/ui-strings/src/main/res/values-sk/strings.xml index 82deefb371..d825518e0f 100644 --- a/libraries/ui-strings/src/main/res/values-sk/strings.xml +++ b/libraries/ui-strings/src/main/res/values-sk/strings.xml @@ -2980,4 +2980,13 @@ Nemožno spustiť hlasovú správu Chyba pripojenia - nahrávanie pozastavené Použiť formát riadkového kódu + Prepnutie bloku kódu + Prepínanie citácie + Zrušiť odsadenie + Odsadenie + Zobraziť anketu na časovej osi + Toto hlasové vysielanie sa nedá dešifrovať. + Údaje o vašom účte sú spravované samostatne na %1$s. + Účet + Pri aktualizácii vašich predvolieb oznámení došlo k chybe. Skúste prosím znova. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-sq/strings.xml b/libraries/ui-strings/src/main/res/values-sq/strings.xml index 447a2b52d7..50f7543f67 100644 --- a/libraries/ui-strings/src/main/res/values-sq/strings.xml +++ b/libraries/ui-strings/src/main/res/values-sq/strings.xml @@ -2906,4 +2906,13 @@ Gabim lidhjeje - Incizimi u ndal S’mund të nisni një mesazh zanor teksa jeni aktualisht duke incizuar një transmetim të drejtpërdrejtë. Ju lutemi, përfundoni transmetimin tuaj të drejtpërdrejtë, që të mund të nisni incizimin e një mesazhi zanor S’niset dot mesazh zanor + Shfaq/fshih bllok kodi + Shfaq/fshih citim + Hiqe zhvendosjen e kryeradhës + Zhvendos kryeradhën + Shihni pyetësor në rrjedhë kohore + S’arrihet të shfeshtëzohet ky transmetim zanor. + Hollësitë e llogarisë tuaj administrohen më vete, te %1$s. + Llogari + Ndodhi një gabim, kur u përditësuan parapëlqimet tuaja për njoftime. Ju lutemi, riprovoni. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-sv/strings.xml b/libraries/ui-strings/src/main/res/values-sv/strings.xml index 597dd90b2f..1c9688e99c 100644 --- a/libraries/ui-strings/src/main/res/values-sv/strings.xml +++ b/libraries/ui-strings/src/main/res/values-sv/strings.xml @@ -326,7 +326,7 @@ \nLägg till några nu\? Tyvärr har ingen extern applikation hittats som kan fullfölja denna handling. Logga in - Logga in med externt konto + Logga in med samlad inloggning Skicka in Fel användarnamn och/eller lösenord Det här ser inte ut som en giltig e-postadress @@ -1820,7 +1820,7 @@ Skickar det givna meddelandet med snöfall Skickar de givna meddelandet med konfetti Rensa historik - externt konto + samlad inloggning Logga in med %s Skapa konto med %s Fortsätt med %s @@ -2920,4 +2920,12 @@ Kan inte starta röstsändning Startade en röstsändning Använd inline-kodformat + Växla kodblock + Växla citat + Minska indrag + Indrag + Visa omröstning i tidslinjen + Kunder inte avkryptera den här röstsändningen. + Dina kontodetaljer hanteras separat på %1$s. + Konto \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-uk/strings.xml b/libraries/ui-strings/src/main/res/values-uk/strings.xml index 26d38bb324..9df054e1bd 100644 --- a/libraries/ui-strings/src/main/res/values-uk/strings.xml +++ b/libraries/ui-strings/src/main/res/values-uk/strings.xml @@ -1147,7 +1147,7 @@ Ви вийшли Вилучити… Наліпка - Використовувати ботів, мости, віджети та пакунки наліпок + Використовувати боти, мости, віджети та пакунки наліпок Зв\'язок із сервером втрачено Виправлень не знайдено Історія виправлень @@ -3040,4 +3040,13 @@ Не вдалося розпочати запис голосового повідомлення Помилка з\'єднання - Запис призупинено Застосовувати вбудований формат коду + Перемкнути блок коду + Перемкнути цитування + Без відступу + Відступ + Переглянути опитування у стрічці + Неможливо розшифрувати цю голосову трансляцію. + Керування подробицями вашого облікового запису відбувається окремо на %1$s. + Обліковий запис + Сталася помилка під час оновлення налаштувань сповіщень. Повторіть спробу. \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-zh-rCN/strings.xml b/libraries/ui-strings/src/main/res/values-zh-rCN/strings.xml index f3b5854afb..52d5e5f17a 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rCN/strings.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rCN/strings.xml @@ -9,7 +9,7 @@ %1$s 移除了 %2$s %1$s 解封了 %2$s %1$s 封禁了 %2$s - %1$s 更换了他们的头像 + %1$s 更换了其头像 %1$s 将他们的显示名称设置为 %2$s %1$s 将其显示名称从 %2$s 更改为 %3$s %1$s 移除了他们的显示名称(%2$s) @@ -178,7 +178,7 @@ 拒绝 挂断 引用 - 共享 + 分享 语音通话 视频通话 全部标记为已读 @@ -797,10 +797,10 @@ 房间 回应 同意 - 添加反应 - 查看反应 + 添加回应 + 查看回应 反应 - 由用户删除的事件 + 事件被用户删除 创建新房间 修改 请稍候…… @@ -825,7 +825,7 @@ 帮助和关于 (已编辑) - 撤消 + 撤销 断开连接 拒绝 这不是有效的 Matrix 服务器地址 @@ -942,7 +942,7 @@ 消息已移除 显示已移除消息 对已移除消息显示占位符 - 房间管理员主持的事件 + 事件被房间管理员删除 格式错误事件,无法显示 无网络。请检查你的网络连接。 更改网络 @@ -1265,7 +1265,7 @@ 上传 离开房间 - 正在离开房间… + 正在离开房间…… 管理员 协管员 自定义 @@ -1597,7 +1597,7 @@ %d 秒 轮询 - 用%s反应 + 用%s回应 验证结果 是否删除类型 %1$s 的账户数据? \n @@ -1702,7 +1702,7 @@ 从低优先级移除 添加到低优先级 - %2$d 的 %1$d + %1$d / %2$d 旋转和裁剪 添加图像自 授予许可 @@ -1758,7 +1758,7 @@ 权限 查看和更新更改房间各个部分所需的角色。 房间权限 - 此房间不公开。你没有邀请将无法重新加入。 + 此房间不公开。没有邀请,你将无法重新加入。 你保持通话 %s 保持通话 保持 @@ -1883,7 +1883,7 @@ 此别名当前无法被访问。 \n请稍后再试,或询问房间管理员你身份有权访问。 - 无论以何种方式加入 + 依然加入 加入空间 创建空间 暂且略过 @@ -2820,4 +2820,6 @@ 你无法启动语音消息因为你正在录制实时广播。请终止实时广播以开始录制语音消息 无法启动语音消息 结束了投票。 + 你的访问令牌提供对你账户的完全访问权限。勿与任何人分享它。 + 访问令牌 \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/strings.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/strings.xml index b3845e550d..058a4e4507 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/strings.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/strings.xml @@ -2860,4 +2860,13 @@ 您無法開始語音訊息,因為您目前正在錄製直播。請結束您的直播以開始錄製語音訊息 無法開始語音訊息 套用內嵌程式碼格式 + 切換程式碼區塊 + 切換引用 + 取消縮排 + 縮排 + 在時間軸中檢視投票 + 無法解密此語音廣播。 + 您的帳號詳細資訊已單獨於 %1$s 中管理。 + 帳號 + 更新您的通知偏好設定時發生錯誤。請再試一次。 \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values/strings.xml b/libraries/ui-strings/src/main/res/values/strings.xml index 2ee623cf88..62f061f009 100644 --- a/libraries/ui-strings/src/main/res/values/strings.xml +++ b/libraries/ui-strings/src/main/res/values/strings.xml @@ -1817,6 +1817,7 @@ Add by QR code QR code "Creating room…" + You can only invite one email at a time Known Users Suggestions @@ -2561,6 +2562,8 @@ Messages in this room are end-to-end encrypted. Learn more & verify users in their profile. Messages in this chat are end-to-end encrypted. Messages in this chat will be end-to-end encrypted. + Waiting for users to join ${app_name} + Once invited users have joined ${app_name}, you will be able to chat and the room will be end-to-end encrypted Encryption not enabled Encryption is misconfigured The encryption used by this room is not supported diff --git a/libraries/ui-strings/src/main/res/values/strings_eax.xml b/libraries/ui-strings/src/main/res/values/strings_eax.xml index cc6160ce25..0ed87ec9f0 100644 --- a/libraries/ui-strings/src/main/res/values/strings_eax.xml +++ b/libraries/ui-strings/src/main/res/values/strings_eax.xml @@ -2,5 +2,17 @@ + Back + Clear + + Enter your details + Email or username + Show password + Hide password + + What is the address of your server? + You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it. + Server not supported + This server currently doesn\'t support sliding sync. diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 217a3318fa..6236bfd307 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -23,7 +23,7 @@ object Versions { const val compileSdk = 33 const val targetSdk = 33 - const val minSdk = 21 + const val minSdk = 23 val javaCompileVersion = JavaVersion.VERSION_11 val javaLanguageVersion: JavaLanguageVersion = JavaLanguageVersion.of(11) } diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index eec0d97052..1bd6048ea2 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -51,11 +51,11 @@ fun DependencyHandlerScope.composeDependencies(libs: LibrariesForLibs) { fun DependencyHandlerScope.allLibraries() { implementation(project(":libraries:designsystem")) - implementation(project(":libraries:matrix")) + implementation(project(":libraries:matrix:api")) implementation(project(":libraries:matrixui")) implementation(project(":libraries:core")) implementation(project(":libraries:architecture")) - implementation(project(":libraries:dateformatter")) + implementation(project(":libraries:dateformatter:api")) implementation(project(":libraries:di")) } diff --git a/samples/minimal/build.gradle.kts b/samples/minimal/build.gradle.kts index 835c3fc772..a7da2a94c8 100644 --- a/samples/minimal/build.gradle.kts +++ b/samples/minimal/build.gradle.kts @@ -47,12 +47,16 @@ android { dependencies { implementation(libs.androidx.activity.compose) - implementation(projects.libraries.matrix) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrix.impl) + implementation(projects.libraries.sessionStorage.implMemory) implementation(projects.libraries.designsystem) implementation(projects.libraries.architecture) implementation(projects.libraries.core) - implementation(projects.libraries.dateformatter) + implementation(projects.libraries.dateformatter.api) + implementation(projects.libraries.dateformatter.impl) implementation(projects.features.roomlist) implementation(projects.features.login) + implementation(libs.coroutines.core) coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.2") } diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt index 1b5be5ebeb..4eb80e6120 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt @@ -21,7 +21,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import io.element.android.features.login.impl.root.LoginRootPresenter import io.element.android.features.login.impl.root.LoginRootScreen -import io.element.android.libraries.matrix.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService class LoginScreen(private val authenticationService: MatrixAuthenticationService) { @@ -33,7 +33,8 @@ class LoginScreen(private val authenticationService: MatrixAuthenticationService val state = presenter.present() LoginRootScreen( state = state, - modifier = modifier + modifier = modifier, + onBackPressed = {}, ) } } diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index 1b6c79f518..4ba66c51d6 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -29,9 +29,9 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.core.view.WindowCompat import io.element.android.libraries.designsystem.theme.ElementTheme -import io.element.android.libraries.matrix.auth.MatrixAuthenticationService -import io.element.android.libraries.matrix.auth.RustMatrixAuthenticationService -import io.element.android.libraries.matrix.session.PreferencesSessionStore +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService +import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore import kotlinx.coroutines.runBlocking import org.matrix.rustcomponents.sdk.AuthenticationService import java.io.File @@ -45,7 +45,7 @@ class MainActivity : ComponentActivity() { baseDirectory = baseDirectory, coroutineScope = Singleton.appScope, coroutineDispatchers = Singleton.coroutineDispatchers, - sessionStore = PreferencesSessionStore(applicationContext), + sessionStore = InMemorySessionStore(), authService = AuthenticationService(baseDirectory.absolutePath, null, null), ) } diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index 32d9820d4d..bc09cf78e7 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -24,8 +24,8 @@ import io.element.android.features.roomlist.impl.RoomListView import io.element.android.libraries.dateformatter.impl.DateFormatters import io.element.android.libraries.dateformatter.impl.DefaultLastMessageFormatter import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider -import io.element.android.libraries.matrix.MatrixClient -import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.coroutines.launch import kotlinx.datetime.Clock import kotlinx.datetime.TimeZone diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt index fb74d2bacb..006ca8d9e4 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt @@ -17,8 +17,8 @@ package io.element.android.samples.minimal import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.tracing.TracingConfigurations -import io.element.android.libraries.matrix.tracing.setupTracing +import io.element.android.libraries.matrix.impl.tracing.setupTracing +import io.element.android.libraries.matrix.api.tracing.TracingConfigurations import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope diff --git a/samples/minimal/src/main/res/values-night/themes.xml b/samples/minimal/src/main/res/values-night/themes.xml new file mode 100644 index 0000000000..b059f36a0e --- /dev/null +++ b/samples/minimal/src/main/res/values-night/themes.xml @@ -0,0 +1,20 @@ + + + + +