Browse Source

Merge branch 'develop' into dla/feature/custom_room_notification_settings_list

pull/1621/head
David Langley 11 months ago committed by GitHub
parent
commit
e2adecbcf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/build.gradle.kts
  2. 7
      app/src/main/kotlin/io/element/android/x/di/AppModule.kt
  3. 1
      changelog.d/1375.bugfix
  4. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt
  5. 8
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt
  6. 9
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
  7. 29
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemEventContentKey.kt
  8. 77
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemPresenterFactories.kt
  9. 33
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemPresenterFactory.kt
  10. 7
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt
  11. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt
  12. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt
  13. 4
      gradle/libs.versions.toml
  14. 8
      libraries/di/src/main/kotlin/io/element/android/libraries/di/ApplicationContext.kt
  15. 27
      libraries/di/src/main/kotlin/io/element/android/libraries/di/CacheDirectory.kt
  16. 2
      libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt
  17. 24
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioDetails.kt
  18. 5
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt
  19. 7
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt
  20. 30
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioDetails.kt
  21. 8
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
  22. 2
      libraries/pushstore/impl/build.gradle.kts
  23. 2
      libraries/session-storage/impl/build.gradle.kts
  24. 1
      plugins/src/main/kotlin/extension/CommonExtension.kt
  25. 4
      plugins/src/main/kotlin/io.element.android-compose-application.gradle.kts
  26. 4
      plugins/src/main/kotlin/io.element.android-compose-library.gradle.kts
  27. 4
      plugins/src/main/kotlin/io.element.android-library.gradle.kts
  28. 1
      samples/minimal/build.gradle.kts
  29. 2
      samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt
  30. 2
      tools/detekt/detekt.yml

1
app/build.gradle.kts

@ -203,7 +203,6 @@ dependencies {
implementation(projects.appnav) implementation(projects.appnav)
anvil(projects.anvilcodegen) anvil(projects.anvilcodegen)
coreLibraryDesugaring(libs.android.desugar)
implementation(libs.appyx.core) implementation(libs.appyx.core)
implementation(libs.androidx.splash) implementation(libs.androidx.splash)
implementation(libs.androidx.core) implementation(libs.androidx.core)

7
app/src/main/kotlin/io/element/android/x/di/AppModule.kt

@ -31,6 +31,7 @@ import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.DefaultPreferences import io.element.android.libraries.di.DefaultPreferences
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.x.BuildConfig import io.element.android.x.BuildConfig
@ -51,6 +52,12 @@ object AppModule {
return File(context.filesDir, "sessions") return File(context.filesDir, "sessions")
} }
@Provides
@CacheDirectory
fun providesCacheDirectory(@ApplicationContext context: Context): File {
return context.cacheDir
}
@Provides @Provides
fun providesResources(@ApplicationContext context: Context): Resources { fun providesResources(@ApplicationContext context: Context): Resources {
return context.resources return context.resources

1
changelog.d/1375.bugfix

@ -0,0 +1 @@
Hide keyboard when exiting the chat room screen.

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt

@ -238,7 +238,7 @@ class MessagesFlowNode @AssistedInject constructor(
backstack.push(navTarget) backstack.push(navTarget)
} }
is TimelineItemAudioContent -> { is TimelineItemAudioContent -> {
val mediaSource = event.content.audioSource val mediaSource = event.content.mediaSource
val navTarget = NavTarget.MediaViewer( val navTarget = NavTarget.MediaViewer(
mediaInfo = MediaInfo( mediaInfo = MediaInfo(
name = event.content.body, name = event.content.body,

8
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt

@ -17,6 +17,7 @@
package io.element.android.features.messages.impl package io.element.android.features.messages.impl
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
@ -28,6 +29,8 @@ import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.core.EventId 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.core.UserId
@ -44,6 +47,7 @@ class MessagesNode @AssistedInject constructor(
private val room: MatrixRoom, private val room: MatrixRoom,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val presenterFactory: MessagesPresenter.Factory, private val presenterFactory: MessagesPresenter.Factory,
private val timelineItemPresenterFactories: TimelineItemPresenterFactories,
) : Node(buildContext, plugins = plugins), MessagesNavigator { ) : Node(buildContext, plugins = plugins), MessagesNavigator {
private val presenter = presenterFactory.create(this) private val presenter = presenterFactory.create(this)
@ -106,6 +110,9 @@ class MessagesNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
CompositionLocalProvider(
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,
) {
val state = presenter.present() val state = presenter.present()
MessagesView( MessagesView(
state = state, state = state,
@ -119,4 +126,5 @@ class MessagesNode @AssistedInject constructor(
modifier = modifier, modifier = modifier,
) )
} }
}
} }

9
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -222,6 +223,14 @@ fun MessagesView(
ReinviteDialog( ReinviteDialog(
state = state state = state
) )
// Since the textfield is now based on an Android view, this is no longer done automatically.
// We need to hide the keyboard automatically when navigating out of this screen.
DisposableEffect(Unit) {
onDispose {
localView.hideKeyboard()
}
}
} }
@Composable @Composable

29
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemEventContentKey.kt

@ -0,0 +1,29 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.impl.timeline.di
import dagger.MapKey
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import kotlin.reflect.KClass
/**
* Annotation to add a factory of type [TimelineItemPresenterFactory] to a
* Dagger map multi binding keyed with a subclass of [TimelineItemEventContent].
*/
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class TimelineItemEventContentKey(val value: KClass<out TimelineItemEventContent>)

77
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemPresenterFactories.kt

@ -0,0 +1,77 @@
/*
* 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.messages.impl.timeline.di
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.multibindings.Multibinds
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope
import javax.inject.Inject
/**
* Dagger module that declares the [TimelineItemPresenterFactory] map multi binding.
*
* Its sole purpose is to support the case of an empty map multibinding.
*/
@Module
@ContributesTo(RoomScope::class)
interface TimelineItemPresenterFactoriesModule {
@Multibinds
fun multiBindTimelineItemPresenterFactories(): @JvmSuppressWildcards Map<Class<out TimelineItemEventContent>, TimelineItemPresenterFactory<*, *>>
}
/**
* Wrapper around the [TimelineItemPresenterFactory] map multi binding.
*
* Its only purpose is to provide a nicer type name than:
* `@JvmSuppressWildcards Map<Class<out TimelineItemEventContent>, TimelineItemPresenterFactory<*, *>>`.
*
* A typealias would have been better but typealiases on Dagger types which use @JvmSuppressWildcards
* currently make Dagger crash.
*
* Request this type from Dagger to access the [TimelineItemPresenterFactory] map multibinding.
*/
data class TimelineItemPresenterFactories @Inject constructor(
val factories: @JvmSuppressWildcards Map<Class<out TimelineItemEventContent>, TimelineItemPresenterFactory<*, *>>,
)
/**
* Provides a [TimelineItemPresenterFactories] to the composition.
*/
val LocalTimelineItemPresenterFactories = staticCompositionLocalOf {
TimelineItemPresenterFactories(emptyMap())
}
/**
* Creates and remembers a presenter for the given content.
*
* Will throw if the presenter is not found in the [TimelineItemPresenterFactory] map multi binding.
*/
@Composable
inline fun <reified C : TimelineItemEventContent, reified S : Any> TimelineItemPresenterFactories.rememberPresenter(
content: C
): Presenter<S> = remember(content) {
factories.getValue(C::class.java).let {
@Suppress("UNCHECKED_CAST")
(it as TimelineItemPresenterFactory<C, S>).create(content)
}
}

33
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemPresenterFactory.kt

@ -0,0 +1,33 @@
/*
* 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.messages.impl.timeline.di
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.libraries.architecture.Presenter
/**
* A factory for a [Presenter] associated with a timeline item.
*
* Implementations should be annotated with [AssistedFactory] to be created by Dagger.
*
* @param C The timeline item's [TimelineItemEventContent] subtype.
* @param S The [Presenter]'s state class.
* @return A [Presenter] that produces a state of type [S] for the given content of type [C].
*/
fun interface TimelineItemPresenterFactory<C : TimelineItemEventContent, S : Any> {
fun create(content: C): Presenter<S>
}

7
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt

@ -40,6 +40,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessage
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import java.time.Duration
import javax.inject.Inject import javax.inject.Inject
class TimelineItemContentMessageFactory @Inject constructor( class TimelineItemContentMessageFactory @Inject constructor(
@ -103,11 +104,11 @@ class TimelineItemContentMessageFactory @Inject constructor(
} }
is AudioMessageType -> TimelineItemAudioContent( is AudioMessageType -> TimelineItemAudioContent(
body = messageType.body, body = messageType.body,
audioSource = messageType.source, mediaSource = messageType.source,
duration = messageType.info?.duration?.toMillis() ?: 0L, duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
fileExtension = fileExtensionExtractor.extractFromName(messageType.body) fileExtension = fileExtensionExtractor.extractFromName(messageType.body),
) )
is FileMessageType -> { is FileMessageType -> {
val fileExtension = fileExtensionExtractor.extractFromName(messageType.body) val fileExtension = fileExtensionExtractor.extractFromName(messageType.body)

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt

@ -18,11 +18,12 @@ package io.element.android.features.messages.impl.timeline.model.event
import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import java.time.Duration
data class TimelineItemAudioContent( data class TimelineItemAudioContent(
val body: String, val body: String,
val duration: Long, val duration: Duration,
val audioSource: MediaSource, val mediaSource: MediaSource,
val mimeType: String, val mimeType: String,
val formattedFileSize: String, val formattedFileSize: String,
val fileExtension: String, val fileExtension: String,

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt

@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline.model.event
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import java.time.Duration
open class TimelineItemAudioContentProvider : PreviewParameterProvider<TimelineItemAudioContent> { open class TimelineItemAudioContentProvider : PreviewParameterProvider<TimelineItemAudioContent> {
override val values: Sequence<TimelineItemAudioContent> override val values: Sequence<TimelineItemAudioContent>
@ -34,6 +35,6 @@ fun aTimelineItemAudioContent(fileName: String = "A sound.mp3") = TimelineItemAu
mimeType = MimeTypes.Pdf, mimeType = MimeTypes.Pdf,
formattedFileSize = "100kB", formattedFileSize = "100kB",
fileExtension = "mp3", fileExtension = "mp3",
duration = 100, duration = Duration.ofMillis(100),
audioSource = MediaSource(""), mediaSource = MediaSource(""),
) )

4
gradle/libs.versions.toml

@ -13,7 +13,7 @@ core = "1.12.0"
datastore = "1.0.0" datastore = "1.0.0"
constraintlayout = "2.1.4" constraintlayout = "2.1.4"
constraintlayout_compose = "1.0.1" constraintlayout_compose = "1.0.1"
recyclerview = "1.3.1" recyclerview = "1.3.2"
lifecycle = "2.6.2" lifecycle = "2.6.2"
activity = "1.8.0" activity = "1.8.0"
startup = "1.1.1" startup = "1.1.1"
@ -167,7 +167,7 @@ maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.1"
# Analytics # Analytics
posthog = "com.posthog.android:posthog:2.0.3" posthog = "com.posthog.android:posthog:2.0.3"
sentry = "io.sentry:sentry-android:6.31.0" sentry = "io.sentry:sentry-android:6.32.0"
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:e9cd9adaf18cec52ed851395eb84358b4f9b8d7f" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:e9cd9adaf18cec52ed851395eb84358b4f9b8d7f"
# Emojibase # Emojibase

8
libraries/di/src/main/kotlin/io/element/android/libraries/di/ApplicationContext.kt

@ -18,4 +18,10 @@ package io.element.android.libraries.di
import javax.inject.Qualifier import javax.inject.Qualifier
@Qualifier annotation class ApplicationContext /**
* Qualifies a [Context] object that represents the application context.
*/
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Qualifier
annotation class ApplicationContext

27
libraries/di/src/main/kotlin/io/element/android/libraries/di/CacheDirectory.kt

@ -0,0 +1,27 @@
/*
* 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.di
import javax.inject.Qualifier
/**
* Qualifies a [File] object which represents the application cache directory.
*/
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Qualifier
annotation class CacheDirectory

2
libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt

@ -161,7 +161,7 @@ class DefaultRoomLastMessageFormatterTest {
val sharedContentMessagesTypes = arrayOf( val sharedContentMessagesTypes = arrayOf(
TextMessageType(body, null), TextMessageType(body, null),
VideoMessageType(body, MediaSource("url"), null), VideoMessageType(body, MediaSource("url"), null),
AudioMessageType(body, MediaSource("url"), null), AudioMessageType(body, MediaSource("url"), null, null, false),
ImageMessageType(body, MediaSource("url"), null), ImageMessageType(body, MediaSource("url"), null),
FileMessageType(body, MediaSource("url"), null), FileMessageType(body, MediaSource("url"), null),
LocationMessageType(body, "geo:1,2", null), LocationMessageType(body, "geo:1,2", null),

24
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioDetails.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.media
import java.time.Duration
data class AudioDetails(
val duration: Duration,
val waveform: List<Int>,
)

5
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt

@ -16,6 +16,7 @@
package io.element.android.libraries.matrix.api.timeline.item.event package io.element.android.libraries.matrix.api.timeline.item.event
import io.element.android.libraries.matrix.api.media.AudioDetails
import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.ImageInfo
@ -46,7 +47,9 @@ data class LocationMessageType(
data class AudioMessageType( data class AudioMessageType(
val body: String, val body: String,
val source: MediaSource, val source: MediaSource,
val info: AudioInfo? val info: AudioInfo?,
val details: AudioDetails?,
val isVoiceMessage: Boolean,
) : MessageType ) : MessageType
data class VideoMessageType( data class VideoMessageType(

7
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt

@ -16,9 +16,8 @@
package io.element.android.libraries.matrix.impl package io.element.android.libraries.matrix.impl
import android.content.Context
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.SessionStore
@ -32,8 +31,8 @@ import java.io.File
import javax.inject.Inject import javax.inject.Inject
class RustMatrixClientFactory @Inject constructor( class RustMatrixClientFactory @Inject constructor(
@ApplicationContext private val context: Context,
private val baseDirectory: File, private val baseDirectory: File,
@CacheDirectory private val cacheDirectory: File,
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
private val sessionStore: SessionStore, private val sessionStore: SessionStore,
@ -63,7 +62,7 @@ class RustMatrixClientFactory @Inject constructor(
appCoroutineScope = appCoroutineScope, appCoroutineScope = appCoroutineScope,
dispatchers = coroutineDispatchers, dispatchers = coroutineDispatchers,
baseDirectory = baseDirectory, baseDirectory = baseDirectory,
baseCacheDirectory = context.cacheDir, baseCacheDirectory = cacheDirectory,
clock = clock, clock = clock,
) )
} }

30
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioDetails.kt

@ -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.matrix.impl.media
import io.element.android.libraries.matrix.api.media.AudioDetails
import org.matrix.rustcomponents.sdk.UnstableAudioDetailsContent as RustAudioDetails
fun RustAudioDetails.map(): AudioDetails = AudioDetails(
duration = duration,
waveform = waveform.map { it.toInt() },
)
fun AudioDetails.map(): RustAudioDetails = RustAudioDetails(
duration = duration,
waveform = waveform.map { it.toUShort() }
)

8
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt

@ -75,7 +75,13 @@ class EventMessageMapper {
fun mapMessageType(type: RustMessageType?) = when (type) { fun mapMessageType(type: RustMessageType?) = when (type) {
is RustMessageType.Audio -> { is RustMessageType.Audio -> {
AudioMessageType(type.content.body, type.content.source.map(), type.content.info?.map()) AudioMessageType(
body = type.content.body,
source = type.content.source.map(),
info = type.content.info?.map(),
details = type.content.audio?.map(),
isVoiceMessage = type.content.voice != null,
)
} }
is RustMessageType.File -> { is RustMessageType.File -> {
FileMessageType(type.content.body, type.content.source.map(), type.content.info?.map()) FileMessageType(type.content.body, type.content.source.map(), type.content.info?.map())

2
libraries/pushstore/impl/build.gradle.kts

@ -55,6 +55,4 @@ dependencies {
androidTestImplementation(libs.test.truth) androidTestImplementation(libs.test.truth)
androidTestImplementation(libs.test.runner) androidTestImplementation(libs.test.runner)
androidTestImplementation(projects.libraries.sessionStorage.test) androidTestImplementation(projects.libraries.sessionStorage.test)
coreLibraryDesugaring(libs.android.desugar)
} }

2
libraries/session-storage/impl/build.gradle.kts

@ -45,8 +45,6 @@ dependencies {
testImplementation(libs.test.turbine) testImplementation(libs.test.turbine)
testImplementation(libs.coroutines.test) testImplementation(libs.coroutines.test)
testImplementation(libs.sqldelight.driver.jvm) testImplementation(libs.sqldelight.driver.jvm)
coreLibraryDesugaring(libs.android.desugar)
} }
sqldelight { sqldelight {

1
plugins/src/main/kotlin/extension/CommonExtension.kt

@ -31,7 +31,6 @@ fun CommonExtension<*, *, *, *, *>.androidConfig(project: Project) {
} }
compileOptions { compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
} }

4
plugins/src/main/kotlin/io.element.android-compose-application.gradle.kts

@ -32,9 +32,13 @@ plugins {
android { android {
androidConfig(project) androidConfig(project)
composeConfig(libs) composeConfig(libs)
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
} }
dependencies { dependencies {
commonDependencies(libs) commonDependencies(libs)
composeDependencies(libs) composeDependencies(libs)
coreLibraryDesugaring(libs.android.desugar)
} }

4
plugins/src/main/kotlin/io.element.android-compose-library.gradle.kts

@ -32,9 +32,13 @@ plugins {
android { android {
androidConfig(project) androidConfig(project)
composeConfig(libs) composeConfig(libs)
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
} }
dependencies { dependencies {
commonDependencies(libs) commonDependencies(libs)
composeDependencies(libs) composeDependencies(libs)
coreLibraryDesugaring(libs.android.desugar)
} }

4
plugins/src/main/kotlin/io.element.android-library.gradle.kts

@ -29,8 +29,12 @@ plugins {
android { android {
androidConfig(project) androidConfig(project)
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
} }
dependencies { dependencies {
commonDependencies(libs) commonDependencies(libs)
coreLibraryDesugaring(libs.android.desugar)
} }

1
samples/minimal/build.gradle.kts

@ -65,5 +65,4 @@ dependencies {
implementation(projects.services.toolbox.impl) implementation(projects.services.toolbox.impl)
implementation(projects.libraries.featureflag.impl) implementation(projects.libraries.featureflag.impl)
implementation(libs.coroutines.core) implementation(libs.coroutines.core)
coreLibraryDesugaring(libs.android.desugar)
} }

2
samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt

@ -49,8 +49,8 @@ class MainActivity : ComponentActivity() {
sessionStore = sessionStore, sessionStore = sessionStore,
userAgentProvider = userAgentProvider, userAgentProvider = userAgentProvider,
rustMatrixClientFactory = RustMatrixClientFactory( rustMatrixClientFactory = RustMatrixClientFactory(
context = applicationContext,
baseDirectory = baseDirectory, baseDirectory = baseDirectory,
cacheDirectory = applicationContext.cacheDir,
appCoroutineScope = Singleton.appScope, appCoroutineScope = Singleton.appScope,
coroutineDispatchers = Singleton.coroutineDispatchers, coroutineDispatchers = Singleton.coroutineDispatchers,
sessionStore = sessionStore, sessionStore = sessionStore,

2
tools/detekt/detekt.yml

@ -219,7 +219,7 @@ Compose:
CompositionLocalAllowlist: CompositionLocalAllowlist:
active: true active: true
# You can optionally define a list of CompositionLocals that are allowed here # You can optionally define a list of CompositionLocals that are allowed here
allowedCompositionLocals: LocalCompoundColors, LocalSnackbarDispatcher, LocalCameraPositionState allowedCompositionLocals: LocalCompoundColors, LocalSnackbarDispatcher, LocalCameraPositionState, LocalTimelineItemPresenterFactories
CompositionLocalNaming: CompositionLocalNaming:
active: true active: true
ContentEmitterReturningValues: ContentEmitterReturningValues:

Loading…
Cancel
Save