commit
78a83c0592
87 changed files with 4149 additions and 800 deletions
|
|
@ -39,7 +39,8 @@ sealed interface Async<out T> {
|
|||
suspend fun <T> (suspend () -> T).execute(state: MutableState<Async<T>>) {
|
||||
try {
|
||||
state.value = Async.Loading()
|
||||
state.value = Async.Success(this())
|
||||
val result = this()
|
||||
state.value = Async.Success(result)
|
||||
} catch (error: Throwable) {
|
||||
state.value = Async.Failure(error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ class StableCharSequence(val charSequence: CharSequence) {
|
|||
|
||||
override fun hashCode() = hash
|
||||
override fun equals(other: Any?) = other is StableCharSequence && other.hash == hash
|
||||
|
||||
override fun toString(): String = "StableCharSequence(\"$charSequence\")"
|
||||
}
|
||||
|
||||
fun CharSequence.toStableCharSequence() = StableCharSequence(this)
|
||||
|
|
|
|||
1
libraries/dateformatter/.gitignore
vendored
Normal file
1
libraries/dateformatter/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
43
libraries/dateformatter/build.gradle.kts
Normal file
43
libraries/dateformatter/build.gradle.kts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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")
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.anvil)
|
||||
}
|
||||
|
||||
anvil {
|
||||
generateDaggerFactories.set(true)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.dateformatter"
|
||||
|
||||
dependencies {
|
||||
anvil(projects.anvilcodegen)
|
||||
implementation(libs.dagger)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.anvilannotations)
|
||||
api(libs.datetime)
|
||||
ksp(libs.showkase.processor)
|
||||
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.test.truth)
|
||||
}
|
||||
}
|
||||
0
libraries/dateformatter/consumer-rules.pro
Normal file
0
libraries/dateformatter/consumer-rules.pro
Normal file
21
libraries/dateformatter/proguard-rules.pro
vendored
Normal file
21
libraries/dateformatter/proguard-rules.pro
vendored
Normal file
|
|
@ -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
|
||||
16
libraries/dateformatter/src/main/AndroidManifest.xml
Normal file
16
libraries/dateformatter/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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.
|
||||
-->
|
||||
<manifest />
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.dateformatter
|
||||
|
||||
interface LastMessageFormatter {
|
||||
fun format(timestamp: Long?): String
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.dateformatter.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.TimeZone
|
||||
import java.util.*
|
||||
|
||||
@Module
|
||||
@ContributesTo(AppScope::class)
|
||||
object DateFormatterModule {
|
||||
@Provides
|
||||
fun providesClock(): Clock = Clock.System
|
||||
|
||||
@Provides
|
||||
fun providesLocale(): Locale = Locale.getDefault()
|
||||
|
||||
@Provides
|
||||
fun providesTimezone(): TimeZone = TimeZone.currentSystemDefault()
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.dateformatter.impl
|
||||
|
||||
import android.text.format.DateFormat
|
||||
import android.text.format.DateUtils
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.dateformatter.LastMessageFormatter
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.datetime.toJavaLocalDate
|
||||
import kotlinx.datetime.toJavaLocalDateTime
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import java.time.Period
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultLastMessageFormatter @Inject constructor(
|
||||
private val clock: Clock,
|
||||
private val locale: Locale,
|
||||
private val timezone: TimeZone,
|
||||
) : LastMessageFormatter {
|
||||
private val onlyTimeFormatter: DateTimeFormatter by lazy {
|
||||
val pattern = DateFormat.getBestDateTimePattern(locale, "HH:mm") ?: "HH:mm"
|
||||
DateTimeFormatter.ofPattern(pattern)
|
||||
}
|
||||
|
||||
private val dateWithMonthFormatter: DateTimeFormatter by lazy {
|
||||
val pattern = DateFormat.getBestDateTimePattern(locale, "d MMM") ?: "d MMM"
|
||||
DateTimeFormatter.ofPattern(pattern)
|
||||
}
|
||||
|
||||
private val dateWithYearFormatter: DateTimeFormatter by lazy {
|
||||
val pattern = DateFormat.getBestDateTimePattern(locale, "dd.MM.yyyy") ?: "dd.MM.yyyy"
|
||||
DateTimeFormatter.ofPattern(pattern)
|
||||
}
|
||||
|
||||
override fun format(timestamp: Long?): String {
|
||||
if (timestamp == null) return ""
|
||||
val now: Instant = clock.now()
|
||||
val tsInstant = Instant.fromEpochMilliseconds(timestamp)
|
||||
val nowDateTime = now.toLocalDateTime(timezone)
|
||||
val tsDateTime = tsInstant.toLocalDateTime(timezone)
|
||||
val isSameDay = nowDateTime.date == tsDateTime.date
|
||||
return when {
|
||||
isSameDay -> {
|
||||
onlyTimeFormatter.format(tsDateTime.toJavaLocalDateTime())
|
||||
}
|
||||
else -> {
|
||||
formatDate(tsDateTime, nowDateTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDate(
|
||||
date: LocalDateTime,
|
||||
currentDate: LocalDateTime,
|
||||
): String {
|
||||
val period = Period.between(date.date.toJavaLocalDate(), currentDate.date.toJavaLocalDate())
|
||||
return if (period.years.absoluteValue >= 1) {
|
||||
formatDateWithYear(date)
|
||||
} else if (period.days.absoluteValue < 2 && period.months.absoluteValue < 1) {
|
||||
getRelativeDay(date.toInstant(timezone).toEpochMilliseconds())
|
||||
} else {
|
||||
formatDateWithMonth(date)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDateWithMonth(localDateTime: LocalDateTime): String {
|
||||
return dateWithMonthFormatter.format(localDateTime.toJavaLocalDateTime())
|
||||
}
|
||||
|
||||
private fun formatDateWithYear(localDateTime: LocalDateTime): String {
|
||||
return dateWithYearFormatter.format(localDateTime.toJavaLocalDateTime())
|
||||
}
|
||||
|
||||
private fun getRelativeDay(ts: Long): String {
|
||||
return DateUtils.getRelativeTimeSpanString(
|
||||
ts,
|
||||
clock.now().toEpochMilliseconds(),
|
||||
DateUtils.DAY_IN_MILLIS,
|
||||
DateUtils.FORMAT_SHOW_WEEKDAY
|
||||
)?.toString() ?: ""
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.dateformatter.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.dateformatter.LastMessageFormatter
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.datetime.TimeZone
|
||||
import org.junit.Test
|
||||
import java.util.Locale
|
||||
|
||||
class DefaultLastMessageFormatterTest {
|
||||
|
||||
@Test
|
||||
fun `test null`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
assertThat(formatter.format(null)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test epoch`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
assertThat(formatter.format(0)).isEqualTo("01.01.1970")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test now`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val dat = "1980-04-06T18:35:24.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("18:35")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test one second before`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val dat = "1980-04-06T18:35:23.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("18:35")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test one minute before`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val dat = "1980-04-06T18:34:24.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("18:34")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test one hour before`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val dat = "1980-04-06T17:35:24.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("17:35")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test one day before same time`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val dat = "1980-04-05T18:35:24.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
// TODO DateUtils.getRelativeTimeSpanString returns null.
|
||||
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test one month before same time`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val dat = "1980-03-06T18:35:24.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("6 Mar")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test one year before same time`() {
|
||||
val now = "1980-04-06T18:35:24.00Z"
|
||||
val dat = "1979-04-06T18:35:24.00Z"
|
||||
val formatter = createFormatter(now)
|
||||
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("06.04.1979")
|
||||
}
|
||||
|
||||
/**
|
||||
* Create DefaultLastMessageFormatter and set current time to the provided date.
|
||||
*/
|
||||
private fun createFormatter(@Suppress("SameParameterValue") currentDate: String): LastMessageFormatter {
|
||||
val clock = FakeClock().also { it.givenInstant(Instant.parse(currentDate)) }
|
||||
return DefaultLastMessageFormatter(clock, Locale.US, TimeZone.UTC)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.dateformatter.impl
|
||||
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
class FakeClock : Clock {
|
||||
private var instant: Instant = Instant.fromEpochMilliseconds(0)
|
||||
|
||||
fun givenInstant(instant: Instant) {
|
||||
this.instant = instant
|
||||
}
|
||||
|
||||
override fun now(): Instant = instant
|
||||
}
|
||||
|
|
@ -25,10 +25,18 @@ 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.InMemoryRoomSummaryDataSource
|
||||
import io.element.android.libraries.matrixtest.room.FakeRoomSummaryDataSource
|
||||
import kotlinx.coroutines.delay
|
||||
import org.matrix.rustcomponents.sdk.MediaSource
|
||||
|
||||
class FakeMatrixClient(override val sessionId: SessionId) : MatrixClient {
|
||||
class FakeMatrixClient(
|
||||
override val sessionId: SessionId = SessionId(A_SESSION_ID),
|
||||
private val userDisplayName: Result<String> = Result.success(A_USER_NAME),
|
||||
private val userAvatarURLString: Result<String> = Result.success(AN_AVATAR_URL),
|
||||
val roomSummaryDataSource: RoomSummaryDataSource = FakeRoomSummaryDataSource()
|
||||
) : MatrixClient {
|
||||
|
||||
private var logoutFailure: Throwable? = null
|
||||
|
||||
override fun getRoom(roomId: RoomId): MatrixRoom? {
|
||||
return FakeMatrixRoom(roomId)
|
||||
|
|
@ -39,23 +47,30 @@ class FakeMatrixClient(override val sessionId: SessionId) : MatrixClient {
|
|||
override fun stopSync() = Unit
|
||||
|
||||
override fun roomSummaryDataSource(): RoomSummaryDataSource {
|
||||
return InMemoryRoomSummaryDataSource()
|
||||
return roomSummaryDataSource
|
||||
}
|
||||
|
||||
override fun mediaResolver(): MediaResolver {
|
||||
return FakeMediaResolver()
|
||||
}
|
||||
|
||||
override suspend fun logout() = Unit
|
||||
fun givenLogoutError(failure: Throwable) {
|
||||
logoutFailure = failure
|
||||
}
|
||||
|
||||
override fun userId(): UserId = UserId("")
|
||||
override suspend fun logout() {
|
||||
delay(100)
|
||||
logoutFailure?.let { throw it }
|
||||
}
|
||||
|
||||
override fun userId(): UserId = A_USER_ID
|
||||
|
||||
override suspend fun loadUserDisplayName(): Result<String> {
|
||||
return Result.success("")
|
||||
return userDisplayName
|
||||
}
|
||||
|
||||
override suspend fun loadUserAvatarURLString(): Result<String> {
|
||||
return Result.success("")
|
||||
return userAvatarURLString
|
||||
}
|
||||
|
||||
override suspend fun loadMediaContentForSource(source: MediaSource): Result<ByteArray> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.matrixtest
|
||||
|
||||
import io.element.android.libraries.matrix.core.EventId
|
||||
import io.element.android.libraries.matrix.core.RoomId
|
||||
import io.element.android.libraries.matrix.core.UserId
|
||||
|
||||
const val A_USER_NAME = "alice"
|
||||
const val A_PASSWORD = "password"
|
||||
|
||||
val A_USER_ID = UserId("@alice:server.org")
|
||||
val A_ROOM_ID = RoomId("!aRoomId")
|
||||
val AN_EVENT_ID = EventId("\$anEventId")
|
||||
|
||||
const val A_ROOM_NAME = "A room name"
|
||||
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 AN_AVATAR_URL = "mxc://data"
|
||||
|
||||
const val A_FAILURE_REASON = "There has been a failure"
|
||||
val A_THROWABLE = Throwable(A_FAILURE_REASON)
|
||||
val AN_EXCEPTION = Exception(A_FAILURE_REASON)
|
||||
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrixtest.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 kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class FakeAuthenticationService : MatrixAuthenticationService {
|
||||
private var homeserver: String = A_HOMESERVER
|
||||
private var loginError: Throwable? = null
|
||||
|
||||
override fun isLoggedIn(): Flow<Boolean> {
|
||||
return flowOf(false)
|
||||
}
|
||||
|
||||
override suspend fun getLatestSessionId(): SessionId? {
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun restoreSession(sessionId: SessionId): MatrixClient? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getHomeserver(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun givenHomeserver(homeserver: String) {
|
||||
this.homeserver = homeserver
|
||||
}
|
||||
|
||||
override fun getHomeserverOrDefault(): String {
|
||||
return homeserver
|
||||
}
|
||||
|
||||
override suspend fun setHomeserver(homeserver: String) {
|
||||
delay(100)
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String): SessionId {
|
||||
delay(100)
|
||||
loginError?.let { throw it }
|
||||
return SessionId(A_SESSION_ID)
|
||||
}
|
||||
|
||||
fun givenLoginError(throwable: Throwable?) {
|
||||
loginError = throwable
|
||||
}
|
||||
}
|
||||
|
|
@ -20,17 +20,20 @@ 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 kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
|
||||
class FakeMatrixRoom(
|
||||
override val roomId: RoomId,
|
||||
override val roomId: RoomId = A_ROOM_ID,
|
||||
override val name: String? = null,
|
||||
override val bestName: String = "",
|
||||
override val displayName: String = "",
|
||||
override val topic: String? = null,
|
||||
override val avatarUrl: String? = null
|
||||
override val avatarUrl: String? = null,
|
||||
private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(),
|
||||
) : MatrixRoom {
|
||||
|
||||
override fun syncUpdateFlow(): Flow<Long> {
|
||||
|
|
@ -38,7 +41,7 @@ class FakeMatrixRoom(
|
|||
}
|
||||
|
||||
override fun timeline(): MatrixTimeline {
|
||||
return FakeMatrixTimeline()
|
||||
return matrixTimeline
|
||||
}
|
||||
|
||||
override suspend fun userDisplayName(userId: String): Result<String?> {
|
||||
|
|
@ -50,18 +53,34 @@ class FakeMatrixRoom(
|
|||
}
|
||||
|
||||
override suspend fun sendMessage(message: String): Result<Unit> {
|
||||
TODO("Not yet implemented")
|
||||
delay(100)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
var editMessageParameter: String? = null
|
||||
private set
|
||||
|
||||
override suspend fun editMessage(originalEventId: EventId, message: String): Result<Unit> {
|
||||
TODO("Not yet implemented")
|
||||
editMessageParameter = message
|
||||
delay(100)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
var replyMessageParameter: String? = null
|
||||
private set
|
||||
|
||||
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> {
|
||||
TODO("Not yet implemented")
|
||||
replyMessageParameter = message
|
||||
delay(100)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
var redactEventEventIdParam: EventId? = null
|
||||
private set
|
||||
|
||||
override suspend fun redactEvent(eventId: EventId, reason: String?): Result<Unit> {
|
||||
TODO("Not yet implemented")
|
||||
redactEventEventIdParam = eventId
|
||||
delay(100)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,22 @@ import io.element.android.libraries.matrix.room.RoomSummaryDataSource
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class InMemoryRoomSummaryDataSource : RoomSummaryDataSource {
|
||||
class FakeRoomSummaryDataSource : RoomSummaryDataSource {
|
||||
|
||||
override fun roomSummaries(): StateFlow<List<RoomSummary>> {
|
||||
return MutableStateFlow(emptyList())
|
||||
private val roomSummariesFlow = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||
|
||||
suspend fun postRoomSummary(roomSummaries: List<RoomSummary>) {
|
||||
roomSummariesFlow.emit(roomSummaries)
|
||||
}
|
||||
|
||||
override fun setSlidingSyncRange(range: IntRange) = Unit
|
||||
override fun roomSummaries(): StateFlow<List<RoomSummary>> {
|
||||
return roomSummariesFlow
|
||||
}
|
||||
|
||||
var latestSlidingSyncRange: IntRange? = null
|
||||
private set
|
||||
|
||||
override fun setSlidingSyncRange(range: IntRange) {
|
||||
latestSlidingSyncRange = range
|
||||
}
|
||||
}
|
||||
|
|
@ -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.matrixtest.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
|
||||
|
||||
fun aRoomSummaryFilled(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String = A_ROOM_NAME,
|
||||
isDirect: Boolean = false,
|
||||
avatarURLString: String? = null,
|
||||
lastMessage: CharSequence? = A_MESSAGE,
|
||||
lastMessageTimestamp: Long? = null,
|
||||
unreadNotificationCount: Int = 2,
|
||||
) = RoomSummary.Filled(
|
||||
aRoomSummaryDetail(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
isDirect = isDirect,
|
||||
avatarURLString = avatarURLString,
|
||||
lastMessage = lastMessage,
|
||||
lastMessageTimestamp = lastMessageTimestamp,
|
||||
unreadNotificationCount = unreadNotificationCount,
|
||||
)
|
||||
)
|
||||
|
||||
fun aRoomSummaryDetail(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String = A_ROOM_NAME,
|
||||
isDirect: Boolean = false,
|
||||
avatarURLString: String? = null,
|
||||
lastMessage: CharSequence? = A_MESSAGE,
|
||||
lastMessageTimestamp: Long? = null,
|
||||
unreadNotificationCount: Int = 2,
|
||||
) = RoomSummaryDetails(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
isDirect = isDirect,
|
||||
avatarURLString = avatarURLString,
|
||||
lastMessage = lastMessage,
|
||||
lastMessageTimestamp = lastMessageTimestamp,
|
||||
unreadNotificationCount = unreadNotificationCount,
|
||||
)
|
||||
|
|
@ -19,24 +19,29 @@ package io.element.android.libraries.matrixtest.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 kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import org.matrix.rustcomponents.sdk.TimelineListener
|
||||
|
||||
class FakeMatrixTimeline : MatrixTimeline {
|
||||
override var callback: MatrixTimeline.Callback? = null
|
||||
|
||||
override var callback: MatrixTimeline.Callback?
|
||||
get() = null
|
||||
set(value) {}
|
||||
private var hasMoreToLoadValue: Boolean = true
|
||||
|
||||
fun givenHasMoreToLoad(hasMoreToLoad: Boolean) {
|
||||
this.hasMoreToLoadValue = hasMoreToLoad
|
||||
}
|
||||
|
||||
override val hasMoreToLoad: Boolean
|
||||
get() = true
|
||||
get() = hasMoreToLoadValue
|
||||
|
||||
override fun timelineItems(): Flow<List<MatrixTimelineItem>> {
|
||||
return emptyFlow()
|
||||
}
|
||||
|
||||
override suspend fun paginateBackwards(count: Int): Result<Unit> {
|
||||
delay(100)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue