Merge branch 'develop' into dla/feature/custom_room_notification_settings_list
This commit is contained in:
commit
3ecabd71c0
30 changed files with 306 additions and 39 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
Normal file
1
changelog.d/1375.bugfix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Hide keyboard when exiting the chat room screen.
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,17 +110,21 @@ class MessagesNode @AssistedInject constructor(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
CompositionLocalProvider(
|
||||||
MessagesView(
|
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,
|
||||||
state = state,
|
) {
|
||||||
onBackPressed = this::navigateUp,
|
val state = presenter.present()
|
||||||
onRoomDetailsClicked = this::onRoomDetailsClicked,
|
MessagesView(
|
||||||
onEventClicked = this::onEventClicked,
|
state = state,
|
||||||
onPreviewAttachments = this::onPreviewAttachments,
|
onBackPressed = this::navigateUp,
|
||||||
onUserDataClicked = this::onUserDataClicked,
|
onRoomDetailsClicked = this::onRoomDetailsClicked,
|
||||||
onSendLocationClicked = this::onSendLocationClicked,
|
onEventClicked = this::onEventClicked,
|
||||||
onCreatePollClicked = this::onCreatePollClicked,
|
onPreviewAttachments = this::onPreviewAttachments,
|
||||||
modifier = modifier,
|
onUserDataClicked = this::onUserDataClicked,
|
||||||
)
|
onSendLocationClicked = this::onSendLocationClicked,
|
||||||
|
onCreatePollClicked = this::onCreatePollClicked,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>)
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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(""),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
)
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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() }
|
||||||
|
)
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,12 @@ plugins {
|
||||||
|
|
||||||
android {
|
android {
|
||||||
androidConfig(project)
|
androidConfig(project)
|
||||||
|
compileOptions {
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
commonDependencies(libs)
|
commonDependencies(libs)
|
||||||
|
coreLibraryDesugaring(libs.android.desugar)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue