Open room member avatar in a media viewer (#1911)
* Open room member avatar in viewer. The `MediaViewer` was extracted to its own library module. * Update screenshots * Restore KSP processor in `:libraries:mediaviewer:api`, this should generate Showkase components again. --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
d0cc8c0b2f
commit
bbc4d18a9d
68 changed files with 439 additions and 122 deletions
1
changelog.d/1907.feature
Normal file
1
changelog.d/1907.feature
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Open room member avatar when you click on it inside the member details screen.
|
||||||
|
|
@ -53,6 +53,7 @@ dependencies {
|
||||||
implementation(projects.libraries.dateformatter.api)
|
implementation(projects.libraries.dateformatter.api)
|
||||||
implementation(projects.libraries.eventformatter.api)
|
implementation(projects.libraries.eventformatter.api)
|
||||||
implementation(projects.libraries.mediapickers.api)
|
implementation(projects.libraries.mediapickers.api)
|
||||||
|
implementation(projects.libraries.mediaviewer.api)
|
||||||
implementation(projects.libraries.featureflag.api)
|
implementation(projects.libraries.featureflag.api)
|
||||||
implementation(projects.libraries.mediaupload.api)
|
implementation(projects.libraries.mediaupload.api)
|
||||||
implementation(projects.libraries.permissions.api)
|
implementation(projects.libraries.permissions.api)
|
||||||
|
|
@ -92,6 +93,7 @@ dependencies {
|
||||||
testImplementation(projects.libraries.textcomposer.test)
|
testImplementation(projects.libraries.textcomposer.test)
|
||||||
testImplementation(projects.libraries.voicerecorder.test)
|
testImplementation(projects.libraries.voicerecorder.test)
|
||||||
testImplementation(projects.libraries.mediaplayer.test)
|
testImplementation(projects.libraries.mediaplayer.test)
|
||||||
|
testImplementation(projects.libraries.mediaviewer.test)
|
||||||
testImplementation(libs.test.mockk)
|
testImplementation(libs.test.mockk)
|
||||||
testImplementation(libs.test.junitext)
|
testImplementation(libs.test.junitext)
|
||||||
testImplementation(libs.test.robolectric)
|
testImplementation(libs.test.robolectric)
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,6 @@ import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
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.attachments.preview.AttachmentsPreviewNode
|
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewNode
|
||||||
import io.element.android.features.messages.impl.forward.ForwardMessagesNode
|
import io.element.android.features.messages.impl.forward.ForwardMessagesNode
|
||||||
import io.element.android.features.messages.impl.media.local.MediaInfo
|
|
||||||
import io.element.android.features.messages.impl.media.viewer.MediaViewerNode
|
|
||||||
import io.element.android.features.messages.impl.report.ReportMessageNode
|
import io.element.android.features.messages.impl.report.ReportMessageNode
|
||||||
import io.element.android.features.messages.impl.timeline.debug.EventDebugInfoNode
|
import io.element.android.features.messages.impl.timeline.debug.EventDebugInfoNode
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
|
@ -62,6 +60,8 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
|
@ -180,6 +180,8 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||||
mediaInfo = navTarget.mediaInfo,
|
mediaInfo = navTarget.mediaInfo,
|
||||||
mediaSource = navTarget.mediaSource,
|
mediaSource = navTarget.mediaSource,
|
||||||
thumbnailSource = navTarget.thumbnailSource,
|
thumbnailSource = navTarget.thumbnailSource,
|
||||||
|
canDownload = true,
|
||||||
|
canShare = true,
|
||||||
)
|
)
|
||||||
createNode<MediaViewerNode>(buildContext, listOf(inputs))
|
createNode<MediaViewerNode>(buildContext, listOf(inputs))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ package io.element.android.features.messages.impl.attachments.preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
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.media.local.LocalMedia
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
import io.element.android.features.messages.impl.media.local.MediaInfo
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
import io.element.android.features.messages.impl.media.local.aFileInfo
|
import io.element.android.libraries.mediaviewer.api.local.aFileInfo
|
||||||
import io.element.android.features.messages.impl.media.local.anImageInfo
|
import io.element.android.libraries.mediaviewer.api.local.anImageInfo
|
||||||
|
|
||||||
open class AttachmentsPreviewStateProvider : PreviewParameterProvider<AttachmentsPreviewState> {
|
open class AttachmentsPreviewStateProvider : PreviewParameterProvider<AttachmentsPreviewState> {
|
||||||
override val values: Sequence<AttachmentsPreviewState>
|
override val values: Sequence<AttachmentsPreviewState>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
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.attachments.preview.error.sendAttachmentError
|
import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMediaView
|
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule
|
||||||
import io.element.android.libraries.designsystem.components.ProgressDialog
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
import io.element.android.libraries.designsystem.components.ProgressDialogType
|
import io.element.android.libraries.designsystem.components.ProgressDialogType
|
||||||
|
|
@ -40,6 +39,7 @@ import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaView
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ import androidx.media3.common.util.UnstableApi
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
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.attachments.preview.error.sendAttachmentError
|
import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMediaFactory
|
|
||||||
import io.element.android.features.messages.impl.mentions.MentionSuggestion
|
import io.element.android.features.messages.impl.mentions.MentionSuggestion
|
||||||
import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor
|
import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
|
@ -51,6 +50,7 @@ import io.element.android.libraries.matrix.api.room.Mention
|
||||||
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
|
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
|
||||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory
|
||||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||||
import io.element.android.libraries.textcomposer.model.Message
|
import io.element.android.libraries.textcomposer.model.Message
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||||
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor
|
|
||||||
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
|
|
@ -45,6 +44,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageTy
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
||||||
import io.element.android.libraries.matrix.ui.messages.toHtmlDocument
|
import io.element.android.libraries.matrix.ui.messages.toHtmlDocument
|
||||||
|
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -52,7 +52,7 @@ import kotlin.time.Duration
|
||||||
|
|
||||||
class TimelineItemContentMessageFactory @Inject constructor(
|
class TimelineItemContentMessageFactory @Inject constructor(
|
||||||
private val fileSizeFormatter: FileSizeFormatter,
|
private val fileSizeFormatter: FileSizeFormatter,
|
||||||
private val fileExtensionExtractor: FileExtensionExtractor,
|
private val fileExtensionExtractor: io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor,
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.model.event
|
package io.element.android.features.messages.impl.timeline.model.event
|
||||||
|
|
||||||
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 kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
|
|
@ -29,6 +28,10 @@ data class TimelineItemAudioContent(
|
||||||
val fileExtension: String,
|
val fileExtension: String,
|
||||||
) : TimelineItemEventContent {
|
) : TimelineItemEventContent {
|
||||||
|
|
||||||
val fileExtensionAndSize = formatFileExtensionAndSize(fileExtension, formattedFileSize)
|
val fileExtensionAndSize =
|
||||||
|
io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize(
|
||||||
|
fileExtension,
|
||||||
|
formattedFileSize
|
||||||
|
)
|
||||||
override val type: String = "TimelineItemAudioContent"
|
override val type: String = "TimelineItemAudioContent"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.model.event
|
package io.element.android.features.messages.impl.timeline.model.event
|
||||||
|
|
||||||
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 io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize
|
||||||
|
|
||||||
data class TimelineItemFileContent(
|
data class TimelineItemFileContent(
|
||||||
val body: String,
|
val body: String,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||||
import io.element.android.features.messages.impl.fixtures.aMessageEvent
|
import io.element.android.features.messages.impl.fixtures.aMessageEvent
|
||||||
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory
|
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory
|
||||||
import io.element.android.features.messages.impl.media.FakeLocalMediaFactory
|
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||||
import io.element.android.features.messages.impl.messagesummary.FakeMessageSummaryFormatter
|
import io.element.android.features.messages.impl.messagesummary.FakeMessageSummaryFormatter
|
||||||
|
|
@ -80,6 +79,7 @@ import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||||
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
|
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
|
||||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||||
|
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory
|
||||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
||||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
|
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,13 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents
|
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents
|
||||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter
|
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter
|
||||||
import io.element.android.features.messages.impl.attachments.preview.SendActionState
|
import io.element.android.features.messages.impl.attachments.preview.SendActionState
|
||||||
import io.element.android.features.messages.impl.fixtures.aLocalMedia
|
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
|
||||||
|
|
@ -16,22 +16,10 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.fixtures
|
package io.element.android.features.messages.impl.fixtures
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
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.media.local.LocalMedia
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
import io.element.android.features.messages.impl.media.local.MediaInfo
|
|
||||||
import io.element.android.features.messages.impl.media.local.anImageInfo
|
|
||||||
|
|
||||||
fun aLocalMedia(
|
|
||||||
uri: Uri,
|
|
||||||
mediaInfo: MediaInfo = anImageInfo(),
|
|
||||||
) = LocalMedia(
|
|
||||||
uri = uri,
|
|
||||||
info = mediaInfo
|
|
||||||
)
|
|
||||||
|
|
||||||
fun aMediaAttachment(localMedia: LocalMedia, compressIfPossible: Boolean = true) = Attachment.Media(
|
fun aMediaAttachment(localMedia: LocalMedia, compressIfPossible: Boolean = true) = Attachment.Media(
|
||||||
localMedia = localMedia,
|
localMedia = localMedia,
|
||||||
compressIfPossible = compressIfPossible,
|
compressIfPossible = compressIfPossible,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -32,7 +32,6 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli
|
||||||
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemDaySeparatorFactory
|
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemDaySeparatorFactory
|
||||||
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory
|
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory
|
||||||
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
||||||
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractorWithoutValidation
|
|
||||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter
|
||||||
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
|
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
|
||||||
|
|
@ -40,6 +39,7 @@ import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
|
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import app.cash.turbine.ReceiveTurbine
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
import io.element.android.features.messages.impl.media.FakeLocalMediaFactory
|
|
||||||
import io.element.android.features.messages.impl.mentions.MentionSuggestion
|
import io.element.android.features.messages.impl.mentions.MentionSuggestion
|
||||||
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
|
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl
|
||||||
|
|
@ -67,6 +66,7 @@ import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||||
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
|
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
|
||||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||||
|
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory
|
||||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
||||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
|
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||||
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractorWithoutValidation
|
|
||||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
|
|
@ -55,6 +54,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageT
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
|
import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
|
||||||
|
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ dependencies {
|
||||||
implementation(projects.libraries.androidutils)
|
implementation(projects.libraries.androidutils)
|
||||||
implementation(projects.libraries.mediapickers.api)
|
implementation(projects.libraries.mediapickers.api)
|
||||||
implementation(projects.libraries.mediaupload.api)
|
implementation(projects.libraries.mediaupload.api)
|
||||||
|
implementation(projects.libraries.mediaviewer.api)
|
||||||
implementation(projects.libraries.featureflag.api)
|
implementation(projects.libraries.featureflag.api)
|
||||||
implementation(projects.libraries.permissions.api)
|
implementation(projects.libraries.permissions.api)
|
||||||
implementation(projects.libraries.preferences.api)
|
implementation(projects.libraries.preferences.api)
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,17 @@ import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditNode
|
||||||
import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode
|
import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode
|
||||||
import io.element.android.features.roomdetails.impl.members.RoomMemberListNode
|
import io.element.android.features.roomdetails.impl.members.RoomMemberListNode
|
||||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode
|
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode
|
||||||
|
import io.element.android.features.roomdetails.impl.members.details.avatar.RoomMemberAvatarPreviewNode
|
||||||
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode
|
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode
|
||||||
import io.element.android.libraries.architecture.BackstackNode
|
import io.element.android.libraries.architecture.BackstackNode
|
||||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
|
|
@ -79,6 +84,9 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class RoomMemberDetails(val roomMemberId: UserId) : NavTarget
|
data class RoomMemberDetails(val roomMemberId: UserId) : NavTarget
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class MemberAvatarPreview(val userName: String, val avatarUrl: String) : NavTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
|
|
@ -100,6 +108,10 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
override fun openRoomNotificationSettings() {
|
override fun openRoomNotificationSettings() {
|
||||||
backstack.push(NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = false))
|
backstack.push(NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openAvatarPreview(username: String, url: String) {
|
||||||
|
backstack.push(NavTarget.MemberAvatarPreview(username, url))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createNode<RoomDetailsNode>(buildContext, listOf(roomDetailsCallback))
|
createNode<RoomDetailsNode>(buildContext, listOf(roomDetailsCallback))
|
||||||
}
|
}
|
||||||
|
|
@ -136,9 +148,31 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
is NavTarget.RoomMemberDetails -> {
|
is NavTarget.RoomMemberDetails -> {
|
||||||
val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId))
|
val callback = object : RoomMemberDetailsNode.Callback {
|
||||||
|
override fun openAvatarPreview(username: String, avatarUrl: String) {
|
||||||
|
backstack.push(NavTarget.MemberAvatarPreview(username, avatarUrl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId), callback)
|
||||||
createNode<RoomMemberDetailsNode>(buildContext, plugins)
|
createNode<RoomMemberDetailsNode>(buildContext, plugins)
|
||||||
}
|
}
|
||||||
|
is NavTarget.MemberAvatarPreview -> {
|
||||||
|
// We need to fake the MimeType here for the viewer to work.
|
||||||
|
val mimeType = MimeTypes.Images
|
||||||
|
val input = MediaViewerNode.Inputs(
|
||||||
|
mediaInfo = MediaInfo(
|
||||||
|
name = navTarget.userName,
|
||||||
|
mimeType = mimeType,
|
||||||
|
formattedFileSize = "",
|
||||||
|
fileExtension = ""
|
||||||
|
),
|
||||||
|
mediaSource = MediaSource(url = navTarget.avatarUrl),
|
||||||
|
thumbnailSource = MediaSource(url = navTarget.avatarUrl),
|
||||||
|
canDownload = false,
|
||||||
|
canShare = false,
|
||||||
|
)
|
||||||
|
createNode<RoomMemberAvatarPreviewNode>(buildContext, listOf(input))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||||
fun openInviteMembers()
|
fun openInviteMembers()
|
||||||
fun editRoomDetails()
|
fun editRoomDetails()
|
||||||
fun openRoomNotificationSettings()
|
fun openRoomNotificationSettings()
|
||||||
|
fun openAvatarPreview(username: String, url: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val callbacks = plugins<Callback>()
|
private val callbacks = plugins<Callback>()
|
||||||
|
|
@ -110,6 +111,10 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||||
callbacks.forEach { it.editRoomDetails() }
|
callbacks.forEach { it.editRoomDetails() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openAvatarPreview(username: String, url: String) {
|
||||||
|
callbacks.forEach { it.openAvatarPreview(username, url) }
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -140,6 +145,7 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||||
openRoomMemberList = ::openRoomMemberList,
|
openRoomMemberList = ::openRoomMemberList,
|
||||||
openRoomNotificationSettings = ::openRoomNotificationSettings,
|
openRoomNotificationSettings = ::openRoomNotificationSettings,
|
||||||
invitePeople = ::invitePeople,
|
invitePeople = ::invitePeople,
|
||||||
|
openAvatarPreview = ::openAvatarPreview,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ fun RoomDetailsView(
|
||||||
openRoomMemberList: () -> Unit,
|
openRoomMemberList: () -> Unit,
|
||||||
openRoomNotificationSettings: () -> Unit,
|
openRoomNotificationSettings: () -> Unit,
|
||||||
invitePeople: () -> Unit,
|
invitePeople: () -> Unit,
|
||||||
|
openAvatarPreview: (username: String, url: String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
fun onShareMember() {
|
fun onShareMember() {
|
||||||
|
|
@ -132,7 +133,12 @@ fun RoomDetailsView(
|
||||||
RoomMemberHeaderSection(
|
RoomMemberHeaderSection(
|
||||||
avatarUrl = state.roomAvatarUrl ?: member.avatarUrl,
|
avatarUrl = state.roomAvatarUrl ?: member.avatarUrl,
|
||||||
userId = member.userId.value,
|
userId = member.userId.value,
|
||||||
userName = state.roomName
|
userName = state.roomName,
|
||||||
|
openAvatarPreview = {
|
||||||
|
if (member.avatarUrl != null) {
|
||||||
|
openAvatarPreview(member.displayName ?: member.userId.value, member.avatarUrl!!)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
RoomMemberMainActionsSection(onShareUser = ::onShareMember)
|
RoomMemberMainActionsSection(onShareUser = ::onShareMember)
|
||||||
}
|
}
|
||||||
|
|
@ -411,5 +417,6 @@ private fun ContentToPreview(state: RoomDetailsState) {
|
||||||
openRoomMemberList = {},
|
openRoomMemberList = {},
|
||||||
openRoomNotificationSettings = {},
|
openRoomNotificationSettings = {},
|
||||||
invitePeople = {},
|
invitePeople = {},
|
||||||
|
openAvatarPreview = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,16 @@ class RoomMemberDetailsNode @AssistedInject constructor(
|
||||||
presenterFactory: RoomMemberDetailsPresenter.Factory,
|
presenterFactory: RoomMemberDetailsPresenter.Factory,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
|
|
||||||
|
interface Callback: NodeInputs {
|
||||||
|
fun openAvatarPreview(username: String, avatarUrl: String)
|
||||||
|
}
|
||||||
|
|
||||||
data class RoomMemberDetailsInput(
|
data class RoomMemberDetailsInput(
|
||||||
val roomMemberId: UserId
|
val roomMemberId: UserId
|
||||||
) : NodeInputs
|
) : NodeInputs
|
||||||
|
|
||||||
private val inputs = inputs<RoomMemberDetailsInput>()
|
private val inputs = inputs<RoomMemberDetailsInput>()
|
||||||
|
private val callback = inputs<Callback>()
|
||||||
private val presenter = presenterFactory.create(inputs.roomMemberId)
|
private val presenter = presenterFactory.create(inputs.roomMemberId)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
@ -84,7 +89,8 @@ class RoomMemberDetailsNode @AssistedInject constructor(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
goBack = this::navigateUp,
|
goBack = this::navigateUp,
|
||||||
onShareUser = ::onShareUser
|
onShareUser = ::onShareUser,
|
||||||
|
openAvatarPreview = callback::openAvatarPreview,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package io.element.android.features.roomdetails.impl.members.details
|
package io.element.android.features.roomdetails.impl.members.details
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
|
@ -38,12 +37,13 @@ import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomMemberDetailsView(
|
fun RoomMemberDetailsView(
|
||||||
state: RoomMemberDetailsState,
|
state: RoomMemberDetailsState,
|
||||||
onShareUser: () -> Unit,
|
onShareUser: () -> Unit,
|
||||||
goBack: () -> Unit,
|
goBack: () -> Unit,
|
||||||
|
openAvatarPreview: (username: String, url: String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
|
@ -62,6 +62,9 @@ fun RoomMemberDetailsView(
|
||||||
avatarUrl = state.avatarUrl,
|
avatarUrl = state.avatarUrl,
|
||||||
userId = state.userId,
|
userId = state.userId,
|
||||||
userName = state.userName,
|
userName = state.userName,
|
||||||
|
openAvatarPreview = { avatarUrl ->
|
||||||
|
openAvatarPreview(state.userName ?: state.userId, avatarUrl)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
RoomMemberMainActionsSection(onShareUser = onShareUser)
|
RoomMemberMainActionsSection(onShareUser = onShareUser)
|
||||||
|
|
@ -110,5 +113,6 @@ private fun ContentToPreview(state: RoomMemberDetailsState) {
|
||||||
state = state,
|
state = state,
|
||||||
onShareUser = {},
|
onShareUser = {},
|
||||||
goBack = {},
|
goBack = {},
|
||||||
|
openAvatarPreview = { _, _ -> }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.impl.members.details
|
package io.element.android.features.roomdetails.impl.members.details
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
|
@ -41,13 +42,16 @@ fun RoomMemberHeaderSection(
|
||||||
avatarUrl: String?,
|
avatarUrl: String?,
|
||||||
userId: String,
|
userId: String,
|
||||||
userName: String?,
|
userName: String?,
|
||||||
|
openAvatarPreview: (url: String) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Box(modifier = Modifier.size(70.dp)) {
|
Box(modifier = Modifier.size(70.dp)) {
|
||||||
Avatar(
|
Avatar(
|
||||||
avatarData = AvatarData(userId, userName, avatarUrl, AvatarSize.UserHeader),
|
avatarData = AvatarData(userId, userName, avatarUrl, AvatarSize.UserHeader),
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier
|
||||||
|
.clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) }
|
||||||
|
.fillMaxSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
|
||||||
|
|
@ -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.roomdetails.impl.members.details.avatar
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
|
||||||
|
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerPresenter
|
||||||
|
|
||||||
|
@ContributesNode(RoomScope::class)
|
||||||
|
class RoomMemberAvatarPreviewNode @AssistedInject constructor(
|
||||||
|
@Assisted buildContext: BuildContext,
|
||||||
|
@Assisted plugins: List<Plugin>,
|
||||||
|
presenterFactory: MediaViewerPresenter.Factory,
|
||||||
|
) : MediaViewerNode(buildContext, plugins, presenterFactory)
|
||||||
|
|
@ -60,4 +60,11 @@ object MimeTypes {
|
||||||
else -> OctetStream
|
else -> OctetStream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasSubtype(mimeType: String): Boolean {
|
||||||
|
val components = mimeType.split("/")
|
||||||
|
if (components.size != 2) return false
|
||||||
|
val subType = components.last()
|
||||||
|
return subType.isNotBlank() && subType != "*"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class RustMediaLoader(
|
||||||
val mediaFile = innerClient.getMediaFile(
|
val mediaFile = innerClient.getMediaFile(
|
||||||
mediaSource = mediaSource,
|
mediaSource = mediaSource,
|
||||||
body = body,
|
body = body,
|
||||||
mimeType = mimeType ?: MimeTypes.OctetStream,
|
mimeType = mimeType?.takeIf { MimeTypes.hasSubtype(it) } ?: MimeTypes.OctetStream,
|
||||||
useCache = useCache,
|
useCache = useCache,
|
||||||
tempDir = cacheDirectory.path,
|
tempDir = cacheDirectory.path,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
63
libraries/mediaviewer/api/build.gradle.kts
Normal file
63
libraries/mediaviewer/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-compose-library")
|
||||||
|
alias(libs.plugins.anvil)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
id("kotlin-parcelize")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.libraries.mediaviewer.api"
|
||||||
|
}
|
||||||
|
|
||||||
|
anvil {
|
||||||
|
generateDaggerFactories.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
anvil(projects.anvilcodegen)
|
||||||
|
implementation(projects.anvilannotations)
|
||||||
|
|
||||||
|
implementation(libs.coil.compose)
|
||||||
|
implementation(libs.androidx.media3.exoplayer)
|
||||||
|
implementation(libs.androidx.media3.ui)
|
||||||
|
implementation(libs.coroutines.core)
|
||||||
|
implementation(libs.dagger)
|
||||||
|
implementation(libs.telephoto.zoomableimage)
|
||||||
|
implementation(libs.vanniktech.blurhash)
|
||||||
|
|
||||||
|
implementation(projects.libraries.androidutils)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.libraries.core)
|
||||||
|
implementation(projects.libraries.di)
|
||||||
|
implementation(projects.libraries.designsystem)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(projects.libraries.matrixui)
|
||||||
|
implementation(projects.libraries.uiStrings)
|
||||||
|
|
||||||
|
testImplementation(projects.libraries.matrix.test)
|
||||||
|
testImplementation(projects.libraries.mediaviewer.test)
|
||||||
|
testImplementation(projects.tests.testutils)
|
||||||
|
testImplementation(libs.test.junit)
|
||||||
|
testImplementation(libs.test.truth)
|
||||||
|
testImplementation(libs.test.mockk)
|
||||||
|
testImplementation(libs.test.turbine)
|
||||||
|
testImplementation(libs.coroutines.core)
|
||||||
|
testImplementation(libs.coroutines.test)
|
||||||
|
|
||||||
|
ksp(libs.showkase.processor)
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.helper
|
package io.element.android.libraries.mediaviewer.api.helper
|
||||||
|
|
||||||
fun formatFileExtensionAndSize(extension: String, size: String?): String {
|
fun formatFileExtensionAndSize(extension: String, size: String?): String {
|
||||||
return buildString {
|
return buildString {
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local
|
package io.element.android.libraries.mediaviewer.api.local
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local
|
package io.element.android.libraries.mediaviewer.api.local
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local
|
package io.element.android.libraries.mediaviewer.api.local
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local
|
package io.element.android.libraries.mediaviewer.api.local
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
|
@ -56,10 +56,9 @@ import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.ui.AspectRatioFrameLayout
|
import androidx.media3.ui.AspectRatioFrameLayout
|
||||||
import androidx.media3.ui.PlayerView
|
import androidx.media3.ui.PlayerView
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize
|
import io.element.android.libraries.mediaviewer.api.local.exoplayer.ExoPlayerWrapper
|
||||||
import io.element.android.features.messages.impl.media.local.exoplayer.ExoPlayerWrapper
|
import io.element.android.libraries.mediaviewer.api.local.pdf.PdfViewer
|
||||||
import io.element.android.features.messages.impl.media.local.pdf.PdfViewer
|
import io.element.android.libraries.mediaviewer.api.local.pdf.rememberPdfViewerState
|
||||||
import io.element.android.features.messages.impl.media.local.pdf.rememberPdfViewerState
|
|
||||||
import io.element.android.libraries.core.bool.orFalse
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio
|
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio
|
||||||
|
|
@ -70,6 +69,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||||
import io.element.android.libraries.designsystem.utils.KeepScreenOn
|
import io.element.android.libraries.designsystem.utils.KeepScreenOn
|
||||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
||||||
|
import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import me.saket.telephoto.zoomable.ZoomSpec
|
import me.saket.telephoto.zoomable.ZoomSpec
|
||||||
import me.saket.telephoto.zoomable.ZoomableState
|
import me.saket.telephoto.zoomable.ZoomableState
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local
|
package io.element.android.libraries.mediaviewer.api.local
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local
|
package io.element.android.libraries.mediaviewer.api.local
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local.exoplayer
|
package io.element.android.libraries.mediaviewer.api.local.exoplayer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local.pdf
|
package io.element.android.libraries.mediaviewer.api.local.pdf
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local.pdf
|
package io.element.android.libraries.mediaviewer.api.local.pdf
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local.pdf
|
package io.element.android.libraries.mediaviewer.api.local.pdf
|
||||||
|
|
||||||
import android.graphics.pdf.PdfRenderer
|
import android.graphics.pdf.PdfRenderer
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local.pdf
|
package io.element.android.libraries.mediaviewer.api.local.pdf
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local.pdf
|
package io.element.android.libraries.mediaviewer.api.local.pdf
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.util
|
package io.element.android.libraries.mediaviewer.api.util
|
||||||
|
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.viewer
|
package io.element.android.libraries.mediaviewer.api.viewer
|
||||||
|
|
||||||
sealed interface MediaViewerEvents {
|
sealed interface MediaViewerEvents {
|
||||||
data object SaveOnDisk: MediaViewerEvents
|
data object SaveOnDisk: MediaViewerEvents
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.viewer
|
package io.element.android.libraries.mediaviewer.api.viewer
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -25,14 +25,14 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
import io.element.android.compound.theme.ForcedDarkElementTheme
|
import io.element.android.compound.theme.ForcedDarkElementTheme
|
||||||
import io.element.android.features.messages.impl.media.local.MediaInfo
|
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
class MediaViewerNode @AssistedInject constructor(
|
open class MediaViewerNode @AssistedInject constructor(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
presenterFactory: MediaViewerPresenter.Factory,
|
presenterFactory: MediaViewerPresenter.Factory,
|
||||||
|
|
@ -42,6 +42,8 @@ class MediaViewerNode @AssistedInject constructor(
|
||||||
val mediaInfo: MediaInfo,
|
val mediaInfo: MediaInfo,
|
||||||
val mediaSource: MediaSource,
|
val mediaSource: MediaSource,
|
||||||
val thumbnailSource: MediaSource?,
|
val thumbnailSource: MediaSource?,
|
||||||
|
val canDownload: Boolean,
|
||||||
|
val canShare: Boolean,
|
||||||
) : NodeInputs
|
) : NodeInputs
|
||||||
|
|
||||||
private val inputs: Inputs = inputs()
|
private val inputs: Inputs = inputs()
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.viewer
|
package io.element.android.libraries.mediaviewer.api.viewer
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -29,9 +29,6 @@ import androidx.compose.runtime.setValue
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMediaActions
|
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMediaFactory
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
|
|
@ -39,6 +36,9 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaActions
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -91,6 +91,8 @@ class MediaViewerPresenter @AssistedInject constructor(
|
||||||
thumbnailSource = inputs.thumbnailSource,
|
thumbnailSource = inputs.thumbnailSource,
|
||||||
downloadedMedia = localMedia.value,
|
downloadedMedia = localMedia.value,
|
||||||
snackbarMessage = snackbarMessage,
|
snackbarMessage = snackbarMessage,
|
||||||
|
canDownload = inputs.canDownload,
|
||||||
|
canShare = inputs.canShare,
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -14,18 +14,20 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.viewer
|
package io.element.android.libraries.mediaviewer.api.viewer
|
||||||
|
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
|
||||||
import io.element.android.features.messages.impl.media.local.MediaInfo
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
|
||||||
data class MediaViewerState(
|
data class MediaViewerState(
|
||||||
val mediaInfo: MediaInfo,
|
val mediaInfo: MediaInfo,
|
||||||
val thumbnailSource: MediaSource?,
|
val thumbnailSource: MediaSource?,
|
||||||
val downloadedMedia: Async<LocalMedia>,
|
val downloadedMedia: Async<LocalMedia>,
|
||||||
val snackbarMessage: SnackbarMessage?,
|
val snackbarMessage: SnackbarMessage?,
|
||||||
|
val canDownload: Boolean,
|
||||||
|
val canShare: Boolean,
|
||||||
val eventSink: (MediaViewerEvents) -> Unit,
|
val eventSink: (MediaViewerEvents) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
@ -14,18 +14,18 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.viewer
|
package io.element.android.libraries.mediaviewer.api.viewer
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
|
||||||
import io.element.android.features.messages.impl.media.local.MediaInfo
|
|
||||||
import io.element.android.features.messages.impl.media.local.aFileInfo
|
|
||||||
import io.element.android.features.messages.impl.media.local.aPdfInfo
|
|
||||||
import io.element.android.features.messages.impl.media.local.aVideoInfo
|
|
||||||
import io.element.android.features.messages.impl.media.local.anAudioInfo
|
|
||||||
import io.element.android.features.messages.impl.media.local.anImageInfo
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.aFileInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.aPdfInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.aVideoInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.anAudioInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.anImageInfo
|
||||||
|
|
||||||
open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState> {
|
open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState> {
|
||||||
override val values: Sequence<MediaViewerState>
|
override val values: Sequence<MediaViewerState>
|
||||||
|
|
@ -71,15 +71,27 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
|
||||||
),
|
),
|
||||||
anAudioInfo(),
|
anAudioInfo(),
|
||||||
),
|
),
|
||||||
|
aMediaViewerState(
|
||||||
|
Async.Success(
|
||||||
|
LocalMedia(Uri.EMPTY, anImageInfo())
|
||||||
|
),
|
||||||
|
anImageInfo(),
|
||||||
|
canDownload = false,
|
||||||
|
canShare = false,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aMediaViewerState(
|
fun aMediaViewerState(
|
||||||
downloadedMedia: Async<LocalMedia> = Async.Uninitialized,
|
downloadedMedia: Async<LocalMedia> = Async.Uninitialized,
|
||||||
mediaInfo: MediaInfo = anImageInfo(),
|
mediaInfo: MediaInfo = anImageInfo(),
|
||||||
|
canDownload: Boolean = true,
|
||||||
|
canShare: Boolean = true,
|
||||||
) = MediaViewerState(
|
) = MediaViewerState(
|
||||||
mediaInfo = mediaInfo,
|
mediaInfo = mediaInfo,
|
||||||
thumbnailSource = null,
|
thumbnailSource = null,
|
||||||
downloadedMedia = downloadedMedia,
|
downloadedMedia = downloadedMedia,
|
||||||
snackbarMessage = null
|
snackbarMessage = null,
|
||||||
|
canDownload = canDownload,
|
||||||
|
canShare = canShare,
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.viewer
|
package io.element.android.libraries.mediaviewer.api.viewer
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
|
|
@ -47,11 +47,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import io.element.android.features.messages.impl.R
|
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMediaView
|
|
||||||
import io.element.android.features.messages.impl.media.local.MediaInfo
|
|
||||||
import io.element.android.features.messages.impl.media.local.rememberLocalMediaViewState
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
|
|
@ -66,6 +61,11 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
||||||
|
import io.element.android.libraries.mediaviewer.api.R
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaView
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.rememberLocalMediaViewState
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
|
@ -96,6 +96,8 @@ fun MediaViewerView(
|
||||||
actionsEnabled = state.downloadedMedia is Async.Success,
|
actionsEnabled = state.downloadedMedia is Async.Success,
|
||||||
mimeType = state.mediaInfo.mimeType,
|
mimeType = state.mediaInfo.mimeType,
|
||||||
onBackPressed = onBackPressed,
|
onBackPressed = onBackPressed,
|
||||||
|
canDownload = state.canDownload,
|
||||||
|
canShare = state.canShare,
|
||||||
eventSink = state.eventSink
|
eventSink = state.eventSink
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -162,9 +164,12 @@ private fun rememberShowProgress(downloadedMedia: Async<LocalMedia>): Boolean {
|
||||||
return showProgress
|
return showProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun MediaViewerTopBar(
|
private fun MediaViewerTopBar(
|
||||||
actionsEnabled: Boolean,
|
actionsEnabled: Boolean,
|
||||||
|
canDownload: Boolean,
|
||||||
|
canShare: Boolean,
|
||||||
mimeType: String,
|
mimeType: String,
|
||||||
onBackPressed: () -> Unit,
|
onBackPressed: () -> Unit,
|
||||||
eventSink: (MediaViewerEvents) -> Unit,
|
eventSink: (MediaViewerEvents) -> Unit,
|
||||||
|
|
@ -190,27 +195,31 @@ private fun MediaViewerTopBar(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IconButton(
|
if (canDownload) {
|
||||||
enabled = actionsEnabled,
|
IconButton(
|
||||||
onClick = {
|
enabled = actionsEnabled,
|
||||||
eventSink(MediaViewerEvents.SaveOnDisk)
|
onClick = {
|
||||||
},
|
eventSink(MediaViewerEvents.SaveOnDisk)
|
||||||
) {
|
},
|
||||||
Icon(
|
) {
|
||||||
resourceId = CompoundDrawables.ic_download,
|
Icon(
|
||||||
contentDescription = stringResource(id = CommonStrings.action_save),
|
resourceId = CompoundDrawables.ic_download,
|
||||||
)
|
contentDescription = stringResource(id = CommonStrings.action_save),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
IconButton(
|
if (canShare) {
|
||||||
enabled = actionsEnabled,
|
IconButton(
|
||||||
onClick = {
|
enabled = actionsEnabled,
|
||||||
eventSink(MediaViewerEvents.Share)
|
onClick = {
|
||||||
},
|
eventSink(MediaViewerEvents.Share)
|
||||||
) {
|
},
|
||||||
Icon(
|
) {
|
||||||
resourceId = CompoundDrawables.ic_share_android,
|
Icon(
|
||||||
contentDescription = stringResource(id = CommonStrings.action_share)
|
resourceId = CompoundDrawables.ic_share_android,
|
||||||
)
|
contentDescription = stringResource(id = CommonStrings.action_share)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -16,20 +16,23 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.viewer
|
package io.element.android.libraries.mediaviewer
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import app.cash.molecule.RecompositionMode
|
import app.cash.molecule.RecompositionMode
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.messages.impl.media.FakeLocalMediaActions
|
|
||||||
import io.element.android.features.messages.impl.media.FakeLocalMediaFactory
|
|
||||||
import io.element.android.features.messages.impl.media.local.aFileInfo
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.matrix.test.media.FakeMediaLoader
|
import io.element.android.libraries.matrix.test.media.FakeMediaLoader
|
||||||
import io.element.android.libraries.matrix.test.media.aMediaSource
|
import io.element.android.libraries.matrix.test.media.aMediaSource
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.aFileInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions
|
||||||
|
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory
|
||||||
|
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerEvents
|
||||||
|
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
|
||||||
|
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerPresenter
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -148,12 +151,16 @@ class MediaViewerPresenterTest {
|
||||||
mediaLoader: FakeMediaLoader,
|
mediaLoader: FakeMediaLoader,
|
||||||
localMediaActions: FakeLocalMediaActions,
|
localMediaActions: FakeLocalMediaActions,
|
||||||
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
|
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
|
||||||
|
canShare: Boolean = true,
|
||||||
|
canDownload: Boolean = true,
|
||||||
): MediaViewerPresenter {
|
): MediaViewerPresenter {
|
||||||
return MediaViewerPresenter(
|
return MediaViewerPresenter(
|
||||||
inputs = MediaViewerNode.Inputs(
|
inputs = MediaViewerNode.Inputs(
|
||||||
mediaInfo = TESTED_MEDIA_INFO,
|
mediaInfo = TESTED_MEDIA_INFO,
|
||||||
mediaSource = aMediaSource(),
|
mediaSource = aMediaSource(),
|
||||||
thumbnailSource = null
|
thumbnailSource = null,
|
||||||
|
canShare = canShare,
|
||||||
|
canDownload = canDownload,
|
||||||
),
|
),
|
||||||
localMediaFactory = localMediaFactory,
|
localMediaFactory = localMediaFactory,
|
||||||
mediaLoader = mediaLoader,
|
mediaLoader = mediaLoader,
|
||||||
52
libraries/mediaviewer/impl/build.gradle.kts
Normal file
52
libraries/mediaviewer/impl/build.gradle.kts
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-compose-library")
|
||||||
|
alias(libs.plugins.anvil)
|
||||||
|
id("kotlin-parcelize")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.libraries.mediaviewer.impl"
|
||||||
|
}
|
||||||
|
|
||||||
|
anvil {
|
||||||
|
generateDaggerFactories.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
anvil(projects.anvilcodegen)
|
||||||
|
implementation(projects.anvilannotations)
|
||||||
|
|
||||||
|
implementation(libs.coroutines.core)
|
||||||
|
implementation(libs.dagger)
|
||||||
|
|
||||||
|
api(projects.libraries.mediaviewer.api)
|
||||||
|
implementation(projects.libraries.androidutils)
|
||||||
|
implementation(projects.libraries.core)
|
||||||
|
implementation(projects.libraries.di)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
|
||||||
|
testImplementation(projects.libraries.matrix.test)
|
||||||
|
testImplementation(projects.libraries.mediaviewer.test)
|
||||||
|
testImplementation(projects.tests.testutils)
|
||||||
|
testImplementation(libs.test.junit)
|
||||||
|
testImplementation(libs.test.truth)
|
||||||
|
testImplementation(libs.test.mockk)
|
||||||
|
testImplementation(libs.test.turbine)
|
||||||
|
testImplementation(libs.coroutines.core)
|
||||||
|
testImplementation(libs.coroutines.test)
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local
|
package io.element.android.libraries.mediaviewer.local
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
|
|
@ -43,6 +43,8 @@ import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
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.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaActions
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -14,13 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media.local
|
package io.element.android.libraries.mediaviewer.local
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor
|
|
||||||
import io.element.android.libraries.androidutils.file.getFileName
|
import io.element.android.libraries.androidutils.file.getFileName
|
||||||
import io.element.android.libraries.androidutils.file.getFileSize
|
import io.element.android.libraries.androidutils.file.getFileSize
|
||||||
import io.element.android.libraries.androidutils.file.getMimeType
|
import io.element.android.libraries.androidutils.file.getMimeType
|
||||||
|
|
@ -30,6 +29,10 @@ 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.matrix.api.media.MediaFile
|
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||||
import io.element.android.libraries.matrix.api.media.toFile
|
import io.element.android.libraries.matrix.api.media.toFile
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
30
libraries/mediaviewer/test/build.gradle.kts
Normal file
30
libraries/mediaviewer/test/build.gradle.kts
Normal file
|
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-compose-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.libraries.mediaviewer.test"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(projects.libraries.mediaviewer.impl)
|
||||||
|
implementation(projects.libraries.core)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
}
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media
|
package io.element.android.libraries.mediaviewer.test
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMediaActions
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaActions
|
||||||
import io.element.android.tests.testutils.simulateLongTask
|
import io.element.android.tests.testutils.simulateLongTask
|
||||||
|
|
||||||
class FakeLocalMediaActions : LocalMediaActions {
|
class FakeLocalMediaActions : LocalMediaActions {
|
||||||
|
|
@ -14,17 +14,17 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.media
|
package io.element.android.libraries.mediaviewer.test
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import io.element.android.features.messages.impl.fixtures.aLocalMedia
|
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
|
||||||
import io.element.android.features.messages.impl.media.local.LocalMediaFactory
|
|
||||||
import io.element.android.features.messages.impl.media.local.MediaInfo
|
|
||||||
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor
|
|
||||||
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractorWithoutValidation
|
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor
|
||||||
|
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation
|
||||||
|
|
||||||
class FakeLocalMediaFactory(
|
class FakeLocalMediaFactory(
|
||||||
private val localMediaUri: Uri,
|
private val localMediaUri: Uri,
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.mediaviewer.test.viewer
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.local.anImageInfo
|
||||||
|
|
||||||
|
fun aLocalMedia(
|
||||||
|
uri: Uri,
|
||||||
|
mediaInfo: MediaInfo = anImageInfo(),
|
||||||
|
) = LocalMedia(
|
||||||
|
uri = uri,
|
||||||
|
info = mediaInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
|
@ -105,6 +105,7 @@ fun DependencyHandlerScope.allLibrariesImpl() {
|
||||||
implementation(project(":libraries:cryptography:impl"))
|
implementation(project(":libraries:cryptography:impl"))
|
||||||
implementation(project(":libraries:voicerecorder:impl"))
|
implementation(project(":libraries:voicerecorder:impl"))
|
||||||
implementation(project(":libraries:mediaplayer:impl"))
|
implementation(project(":libraries:mediaplayer:impl"))
|
||||||
|
implementation(project(":libraries:mediaviewer:impl"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DependencyHandlerScope.allServicesImpl() {
|
fun DependencyHandlerScope.allServicesImpl() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0a475687a9b1684159dba1a08f9737c4f49a19c4b7d49cbf4d0b698403fbc84c
|
||||||
|
size 394434
|
||||||
Loading…
Add table
Add a link
Reference in a new issue