From bbc4d18a9df6a410f299c462f1bd39355d34eb32 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 28 Nov 2023 18:39:41 +0100 Subject: [PATCH] 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 --- changelog.d/1907.feature | 1 + features/messages/impl/build.gradle.kts | 2 + .../messages/impl/MessagesFlowNode.kt | 6 +- .../messages/impl/attachments/Attachment.kt | 2 +- .../AttachmentsPreviewStateProvider.kt | 8 +-- .../preview/AttachmentsPreviewView.kt | 2 +- .../MessageComposerPresenter.kt | 2 +- .../TimelineItemContentMessageFactory.kt | 4 +- .../model/event/TimelineItemAudioContent.kt | 7 ++- .../model/event/TimelineItemFileContent.kt | 2 +- .../messages/impl/MessagesPresenterTest.kt | 2 +- .../AttachmentsPreviewPresenterTest.kt | 4 +- .../{media.kt => aMediaAttachment.kt} | 14 +---- .../impl/fixtures/timelineItemsFactory.kt | 2 +- .../MessageComposerPresenterTest.kt | 2 +- .../TimelineItemContentMessageFactoryTest.kt | 2 +- features/roomdetails/impl/build.gradle.kts | 1 + .../roomdetails/impl/RoomDetailsFlowNode.kt | 36 ++++++++++- .../roomdetails/impl/RoomDetailsNode.kt | 6 ++ .../roomdetails/impl/RoomDetailsView.kt | 9 ++- .../members/details/RoomMemberDetailsNode.kt | 8 ++- .../members/details/RoomMemberDetailsView.kt | 8 ++- .../details/RoomMemberHeaderSection.kt | 6 +- .../avatar/RoomMemberAvatarPreviewNode.kt | 33 ++++++++++ .../libraries/core/mimetype/MimeTypes.kt | 7 +++ .../matrix/impl/media/RustMediaLoader.kt | 2 +- libraries/mediaviewer/api/build.gradle.kts | 63 +++++++++++++++++++ .../api}/helper/fileExtensionAndSize.kt | 2 +- .../mediaviewer/api}/local/LocalMedia.kt | 2 +- .../api}/local/LocalMediaActions.kt | 2 +- .../api}/local/LocalMediaFactory.kt | 2 +- .../mediaviewer/api}/local/LocalMediaView.kt | 10 +-- .../api}/local/LocalMediaViewState.kt | 2 +- .../mediaviewer/api}/local/MediaInfo.kt | 2 +- .../api}/local/exoplayer/ExoPlayerWrapper.kt | 2 +- .../local/pdf/ParcelFileDescriptorFactory.kt | 2 +- .../mediaviewer/api}/local/pdf/PdfPage.kt | 2 +- .../api}/local/pdf/PdfRendererManager.kt | 2 +- .../mediaviewer/api}/local/pdf/PdfViewer.kt | 2 +- .../api}/local/pdf/PdfViewerState.kt | 2 +- .../api}/util/FileExtensionExtractor.kt | 2 +- .../api}/viewer/MediaViewerEvents.kt | 2 +- .../api}/viewer/MediaViewerNode.kt | 8 ++- .../api}/viewer/MediaViewerPresenter.kt | 10 +-- .../api}/viewer/MediaViewerState.kt | 8 ++- .../api}/viewer/MediaViewerStateProvider.kt | 30 ++++++--- .../api}/viewer/MediaViewerView.kt | 61 ++++++++++-------- .../src/main/res/drawable/ic_apk_install.xml | 0 .../mediaviewer}/MediaViewerPresenterTest.kt | 17 +++-- libraries/mediaviewer/impl/build.gradle.kts | 52 +++++++++++++++ .../local/AndroidLocalMediaActions.kt | 4 +- .../local/AndroidLocalMediaFactory.kt | 7 ++- libraries/mediaviewer/test/build.gradle.kts | 30 +++++++++ .../test}/FakeLocalMediaActions.kt | 6 +- .../test}/FakeLocalMediaFactory.kt | 14 ++--- .../mediaviewer/test/viewer/media.kt | 31 +++++++++ .../kotlin/extension/DependencyHandleScope.kt | 1 + ...iaViewerView_0_null_0,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_1,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_10,NEXUS_5,1.0,en].png | 3 + ...iaViewerView_0_null_2,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_3,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_4,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_5,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_6,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_7,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_8,NEXUS_5,1.0,en].png} | 0 ...iaViewerView_0_null_9,NEXUS_5,1.0,en].png} | 0 68 files changed, 439 insertions(+), 122 deletions(-) create mode 100644 changelog.d/1907.feature rename features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/{media.kt => aMediaAttachment.kt} (71%) create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/avatar/RoomMemberAvatarPreviewNode.kt create mode 100644 libraries/mediaviewer/api/build.gradle.kts rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/helper/fileExtensionAndSize.kt (93%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/LocalMedia.kt (93%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/LocalMediaActions.kt (95%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/LocalMediaFactory.kt (95%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/LocalMediaView.kt (96%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/LocalMediaViewState.kt (94%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/MediaInfo.kt (95%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/exoplayer/ExoPlayerWrapper.kt (95%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/pdf/ParcelFileDescriptorFactory.kt (94%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/pdf/PdfPage.kt (98%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/pdf/PdfRendererManager.kt (97%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/pdf/PdfViewer.kt (98%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/local/pdf/PdfViewerState.kt (97%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/util/FileExtensionExtractor.kt (96%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/viewer/MediaViewerEvents.kt (93%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/viewer/MediaViewerNode.kt (89%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/viewer/MediaViewerPresenter.kt (94%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/viewer/MediaViewerState.kt (80%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/viewer/MediaViewerStateProvider.kt (72%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api}/viewer/MediaViewerView.kt (84%) rename {features/messages/impl => libraries/mediaviewer/api}/src/main/res/drawable/ic_apk_install.xml (100%) rename {features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/viewer => libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer}/MediaViewerPresenterTest.kt (90%) create mode 100644 libraries/mediaviewer/impl/build.gradle.kts rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer}/local/AndroidLocalMediaActions.kt (97%) rename {features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer}/local/AndroidLocalMediaFactory.kt (88%) create mode 100644 libraries/mediaviewer/test/build.gradle.kts rename {features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test}/FakeLocalMediaActions.kt (88%) rename {features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media => libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test}/FakeLocalMediaFactory.kt (76%) create mode 100644 libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/media.kt rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_10,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png => ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png} (100%) diff --git a/changelog.d/1907.feature b/changelog.d/1907.feature new file mode 100644 index 0000000000..9ee2cba969 --- /dev/null +++ b/changelog.d/1907.feature @@ -0,0 +1 @@ +Open room member avatar when you click on it inside the member details screen. diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index d9eb0fd801..e392c96e04 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -53,6 +53,7 @@ dependencies { implementation(projects.libraries.dateformatter.api) implementation(projects.libraries.eventformatter.api) implementation(projects.libraries.mediapickers.api) + implementation(projects.libraries.mediaviewer.api) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.mediaupload.api) implementation(projects.libraries.permissions.api) @@ -92,6 +93,7 @@ dependencies { testImplementation(projects.libraries.textcomposer.test) testImplementation(projects.libraries.voicerecorder.test) testImplementation(projects.libraries.mediaplayer.test) + testImplementation(projects.libraries.mediaviewer.test) testImplementation(libs.test.mockk) testImplementation(libs.test.junitext) testImplementation(libs.test.robolectric) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 6176e5b598..7957eb2200 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -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.preview.AttachmentsPreviewNode 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.timeline.debug.EventDebugInfoNode 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.media.MediaSource 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.parcelize.Parcelize @@ -180,6 +180,8 @@ class MessagesFlowNode @AssistedInject constructor( mediaInfo = navTarget.mediaInfo, mediaSource = navTarget.mediaSource, thumbnailSource = navTarget.thumbnailSource, + canDownload = true, + canShare = true, ) createNode(buildContext, listOf(inputs)) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt index 8739a45201..55c8033636 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt @@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.attachments import android.os.Parcelable 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 @Immutable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index de7f5cd47b..37b2ee9c78 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -19,10 +19,10 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.core.net.toUri import io.element.android.features.messages.impl.attachments.Attachment -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.anImageInfo +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.anImageInfo open class AttachmentsPreviewStateProvider : PreviewParameterProvider { override val values: Sequence diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 1bb8428e02..2e10eab9b6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp 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.media.local.LocalMediaView import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule import io.element.android.libraries.designsystem.components.ProgressDialog 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.theme.components.Scaffold 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 @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index dd2537ff18..04ca34ac08 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -34,7 +34,6 @@ import androidx.media3.common.util.UnstableApi 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.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.MentionSuggestionsProcessor 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.mediapickers.api.PickerProvider 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.PermissionsPresenter import io.element.android.libraries.textcomposer.model.Message diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 803bc260d7..cb11a48d7a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -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.TimelineItemVideoContent 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.core.mimetype.MimeTypes 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.VoiceMessageType 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.toImmutableList import javax.inject.Inject @@ -52,7 +52,7 @@ import kotlin.time.Duration class TimelineItemContentMessageFactory @Inject constructor( private val fileSizeFormatter: FileSizeFormatter, - private val fileExtensionExtractor: FileExtensionExtractor, + private val fileExtensionExtractor: io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor, private val featureFlagService: FeatureFlagService, ) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt index 454bc581dc..aa228efcbd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt @@ -16,7 +16,6 @@ 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 kotlin.time.Duration @@ -29,6 +28,10 @@ data class TimelineItemAudioContent( val fileExtension: String, ) : TimelineItemEventContent { - val fileExtensionAndSize = formatFileExtensionAndSize(fileExtension, formattedFileSize) + val fileExtensionAndSize = + io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize( + fileExtension, + formattedFileSize + ) override val type: String = "TimelineItemAudioContent" } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt index aa35f5a117..04f651a8ad 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt @@ -16,8 +16,8 @@ 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.mediaviewer.api.helper.formatFileExtensionAndSize data class TimelineItemFileContent( val body: String, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 4c5bafa1c6..5855bb47a7 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -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.fixtures.aMessageEvent 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.MessageComposerPresenter 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.mediaupload.api.MediaSender 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.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt index 07d6963441..5b8afcfe37 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt @@ -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.AttachmentsPreviewPresenter 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.test.room.FakeMatrixRoom import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaSender 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.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/media.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMediaAttachment.kt similarity index 71% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/media.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMediaAttachment.kt index ad1afc1af7..fb6eabfee1 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/media.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMediaAttachment.kt @@ -16,22 +16,10 @@ 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.media.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 -) +import io.element.android.libraries.mediaviewer.api.local.LocalMedia fun aMediaAttachment(localMedia: LocalMedia, compressIfPossible: Boolean = true) = Attachment.Media( localMedia = localMedia, compressIfPossible = compressIfPossible, ) - diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/timelineItemsFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/timelineItemsFactory.kt index 18ecb4aea9..879333f690 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/timelineItemsFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/timelineItemsFactory.kt @@ -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.TimelineItemVirtualFactory 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.dateformatter.test.FakeDaySeparatorFormatter 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.matrix.api.timeline.item.event.EventTimelineItem 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 kotlinx.coroutines.test.TestScope diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt index 02b5b12f2e..b2b75d7e38 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt @@ -26,7 +26,6 @@ import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat 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.messagecomposer.AttachmentsState 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.MediaUploadInfo 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.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt index 8c8af4589b..ab2e429d10 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt @@ -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.TimelineItemVideoContent 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.core.mimetype.MimeTypes 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.test.AN_EVENT_ID 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.coroutines.test.runTest import org.junit.Test diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index a2fdfa18c1..e35526ec37 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.mediapickers.api) implementation(projects.libraries.mediaupload.api) + implementation(projects.libraries.mediaviewer.api) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.permissions.api) implementation(projects.libraries.preferences.api) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index dba31ab223..60e302494e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -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.members.RoomMemberListNode 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.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler 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.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 @ContributesNode(RoomScope::class) @@ -79,6 +84,9 @@ class RoomDetailsFlowNode @AssistedInject constructor( @Parcelize 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 { @@ -100,6 +108,10 @@ class RoomDetailsFlowNode @AssistedInject constructor( override fun openRoomNotificationSettings() { backstack.push(NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = false)) } + + override fun openAvatarPreview(username: String, url: String) { + backstack.push(NavTarget.MemberAvatarPreview(username, url)) + } } createNode(buildContext, listOf(roomDetailsCallback)) } @@ -136,9 +148,31 @@ class RoomDetailsFlowNode @AssistedInject constructor( } 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(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(buildContext, listOf(input)) + } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt index 65c2ed0f89..f3fa9e2645 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt @@ -52,6 +52,7 @@ class RoomDetailsNode @AssistedInject constructor( fun openInviteMembers() fun editRoomDetails() fun openRoomNotificationSettings() + fun openAvatarPreview(username: String, url: String) } private val callbacks = plugins() @@ -110,6 +111,10 @@ class RoomDetailsNode @AssistedInject constructor( callbacks.forEach { it.editRoomDetails() } } + private fun openAvatarPreview(username: String, url: String) { + callbacks.forEach { it.openAvatarPreview(username, url) } + } + @Composable override fun View(modifier: Modifier) { val context = LocalContext.current @@ -140,6 +145,7 @@ class RoomDetailsNode @AssistedInject constructor( openRoomMemberList = ::openRoomMemberList, openRoomNotificationSettings = ::openRoomNotificationSettings, invitePeople = ::invitePeople, + openAvatarPreview = ::openAvatarPreview, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index d7e43fdde0..9c7151d4af 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -89,6 +89,7 @@ fun RoomDetailsView( openRoomMemberList: () -> Unit, openRoomNotificationSettings: () -> Unit, invitePeople: () -> Unit, + openAvatarPreview: (username: String, url: String) -> Unit, modifier: Modifier = Modifier, ) { fun onShareMember() { @@ -132,7 +133,12 @@ fun RoomDetailsView( RoomMemberHeaderSection( avatarUrl = state.roomAvatarUrl ?: member.avatarUrl, 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) } @@ -411,5 +417,6 @@ private fun ContentToPreview(state: RoomDetailsState) { openRoomMemberList = {}, openRoomNotificationSettings = {}, invitePeople = {}, + openAvatarPreview = { _, _ -> }, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt index 54b86f973c..8d20ddaf77 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt @@ -46,11 +46,16 @@ class RoomMemberDetailsNode @AssistedInject constructor( presenterFactory: RoomMemberDetailsPresenter.Factory, ) : Node(buildContext, plugins = plugins) { + interface Callback: NodeInputs { + fun openAvatarPreview(username: String, avatarUrl: String) + } + data class RoomMemberDetailsInput( val roomMemberId: UserId ) : NodeInputs private val inputs = inputs() + private val callback = inputs() private val presenter = presenterFactory.create(inputs.roomMemberId) init { @@ -84,7 +89,8 @@ class RoomMemberDetailsNode @AssistedInject constructor( state = state, modifier = modifier, goBack = this::navigateUp, - onShareUser = ::onShareUser + onShareUser = ::onShareUser, + openAvatarPreview = callback::openAvatarPreview, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt index bd65e706b4..5d9871ace1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt @@ -17,7 +17,6 @@ package io.element.android.features.roomdetails.impl.members.details import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets 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.TopAppBar -@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun RoomMemberDetailsView( state: RoomMemberDetailsState, onShareUser: () -> Unit, goBack: () -> Unit, + openAvatarPreview: (username: String, url: String) -> Unit, modifier: Modifier = Modifier, ) { Scaffold( @@ -62,6 +62,9 @@ fun RoomMemberDetailsView( avatarUrl = state.avatarUrl, userId = state.userId, userName = state.userName, + openAvatarPreview = { avatarUrl -> + openAvatarPreview(state.userName ?: state.userId, avatarUrl) + }, ) RoomMemberMainActionsSection(onShareUser = onShareUser) @@ -110,5 +113,6 @@ private fun ContentToPreview(state: RoomMemberDetailsState) { state = state, onShareUser = {}, goBack = {}, + openAvatarPreview = { _, _ -> } ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt index 4bd3f1d0b9..5025f37b65 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt @@ -16,6 +16,7 @@ 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.Column import androidx.compose.foundation.layout.Spacer @@ -41,13 +42,16 @@ fun RoomMemberHeaderSection( avatarUrl: String?, userId: String, userName: String?, + openAvatarPreview: (url: String) -> Unit, modifier: Modifier = Modifier ) { Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Box(modifier = Modifier.size(70.dp)) { Avatar( 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)) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/avatar/RoomMemberAvatarPreviewNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/avatar/RoomMemberAvatarPreviewNode.kt new file mode 100644 index 0000000000..8d186ae96c --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/avatar/RoomMemberAvatarPreviewNode.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.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, + presenterFactory: MediaViewerPresenter.Factory, +) : MediaViewerNode(buildContext, plugins, presenterFactory) diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt index af2bca157b..6a97757b55 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt @@ -60,4 +60,11 @@ object MimeTypes { 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 != "*" + } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt index fab0146f9d..6ae20e06af 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt @@ -82,7 +82,7 @@ class RustMediaLoader( val mediaFile = innerClient.getMediaFile( mediaSource = mediaSource, body = body, - mimeType = mimeType ?: MimeTypes.OctetStream, + mimeType = mimeType?.takeIf { MimeTypes.hasSubtype(it) } ?: MimeTypes.OctetStream, useCache = useCache, tempDir = cacheDirectory.path, ) diff --git a/libraries/mediaviewer/api/build.gradle.kts b/libraries/mediaviewer/api/build.gradle.kts new file mode 100644 index 0000000000..5971f6f0c0 --- /dev/null +++ b/libraries/mediaviewer/api/build.gradle.kts @@ -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) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/helper/fileExtensionAndSize.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/fileExtensionAndSize.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/helper/fileExtensionAndSize.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/fileExtensionAndSize.kt index 251d7a0f06..a2a4caec44 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/helper/fileExtensionAndSize.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/fileExtensionAndSize.kt @@ -14,7 +14,7 @@ * 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 { return buildString { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt index 549842428a..067979dc93 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt @@ -14,7 +14,7 @@ * 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.os.Parcelable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaActions.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaActions.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt index f35af36057..158c748a94 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaActions.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt @@ -14,7 +14,7 @@ * 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 diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt index 36852a5a80..64dfbd03d8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt @@ -14,7 +14,7 @@ * 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 io.element.android.libraries.matrix.api.media.MediaFile diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt index 62ddf353fa..1e81f75e9f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt @@ -14,7 +14,7 @@ * 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.net.Uri @@ -56,10 +56,9 @@ import androidx.media3.common.util.UnstableApi import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.PlayerView import io.element.android.compound.theme.ElementTheme -import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize -import io.element.android.features.messages.impl.media.local.exoplayer.ExoPlayerWrapper -import io.element.android.features.messages.impl.media.local.pdf.PdfViewer -import io.element.android.features.messages.impl.media.local.pdf.rememberPdfViewerState +import io.element.android.libraries.mediaviewer.api.local.exoplayer.ExoPlayerWrapper +import io.element.android.libraries.mediaviewer.api.local.pdf.PdfViewer +import io.element.android.libraries.mediaviewer.api.local.pdf.rememberPdfViewerState import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.mimetype.MimeTypes 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.KeepScreenOn 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 me.saket.telephoto.zoomable.ZoomSpec import me.saket.telephoto.zoomable.ZoomableState diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt index d5af7a78e1..07f891c90c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt @@ -14,7 +14,7 @@ * 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.Stable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/MediaInfo.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt index af0f142bd8..726c9dcf0b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt @@ -14,7 +14,7 @@ * 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 io.element.android.libraries.core.mimetype.MimeTypes diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/exoplayer/ExoPlayerWrapper.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/exoplayer/ExoPlayerWrapper.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt index a69db1ef2c..581a018c43 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/exoplayer/ExoPlayerWrapper.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt @@ -14,7 +14,7 @@ * 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 androidx.media3.common.Player diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/ParcelFileDescriptorFactory.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/ParcelFileDescriptorFactory.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt index 22233b313f..523e8b593c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/ParcelFileDescriptorFactory.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt @@ -14,7 +14,7 @@ * 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.net.Uri diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfPage.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt similarity index 98% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfPage.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt index 0b8caed968..1da6d1a21c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfPage.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt @@ -14,7 +14,7 @@ * 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.Canvas diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfRendererManager.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfRendererManager.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt index 8f6c507eb5..56fe175647 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfRendererManager.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt @@ -14,7 +14,7 @@ * 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.os.ParcelFileDescriptor diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewer.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt similarity index 98% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewer.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt index 9940d22542..1bad0e75f5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewer.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt @@ -14,7 +14,7 @@ * 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.background diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewerState.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewerState.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt index f64374d478..8df8bfdecf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewerState.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt @@ -14,7 +14,7 @@ * 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 androidx.compose.foundation.lazy.LazyListState diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/FileExtensionExtractor.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/FileExtensionExtractor.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt index bdb4e0a23d..81a727909a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/FileExtensionExtractor.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt @@ -14,7 +14,7 @@ * 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 com.squareup.anvil.annotations.ContributesBinding diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt index a3d9632f18..b9f8b0aa4d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.viewer +package io.element.android.libraries.mediaviewer.api.viewer sealed interface MediaViewerEvents { data object SaveOnDisk: MediaViewerEvents diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt similarity index 89% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt index 30440f5ceb..3917d2eba4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt @@ -14,7 +14,7 @@ * 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.ui.Modifier @@ -25,14 +25,14 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode 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.inputs import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.mediaviewer.api.local.MediaInfo @ContributesNode(RoomScope::class) -class MediaViewerNode @AssistedInject constructor( +open class MediaViewerNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, presenterFactory: MediaViewerPresenter.Factory, @@ -42,6 +42,8 @@ class MediaViewerNode @AssistedInject constructor( val mediaInfo: MediaInfo, val mediaSource: MediaSource, val thumbnailSource: MediaSource?, + val canDownload: Boolean, + val canShare: Boolean, ) : NodeInputs private val inputs: Inputs = inputs() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt index 91fff1fa72..90d81a4d0c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt @@ -14,7 +14,7 @@ * 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 androidx.compose.runtime.Composable @@ -29,9 +29,6 @@ import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory 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.Presenter 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.matrix.api.media.MatrixMediaLoader 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 kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -91,6 +91,8 @@ class MediaViewerPresenter @AssistedInject constructor( thumbnailSource = inputs.thumbnailSource, downloadedMedia = localMedia.value, snackbarMessage = snackbarMessage, + canDownload = inputs.canDownload, + canShare = inputs.canShare, eventSink = ::handleEvents ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt similarity index 80% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt index 86abaf648e..4b68f4afcc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt @@ -14,18 +14,20 @@ * 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.designsystem.utils.snackbar.SnackbarMessage 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( val mediaInfo: MediaInfo, val thumbnailSource: MediaSource?, val downloadedMedia: Async, val snackbarMessage: SnackbarMessage?, + val canDownload: Boolean, + val canShare: Boolean, val eventSink: (MediaViewerEvents) -> Unit, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt similarity index 72% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt index 1042261be8..14f792c2c6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt @@ -14,18 +14,18 @@ * 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 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.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 { override val values: Sequence @@ -71,15 +71,27 @@ open class MediaViewerStateProvider : PreviewParameterProvider ), anAudioInfo(), ), + aMediaViewerState( + Async.Success( + LocalMedia(Uri.EMPTY, anImageInfo()) + ), + anImageInfo(), + canDownload = false, + canShare = false, + ), ) } fun aMediaViewerState( downloadedMedia: Async = Async.Uninitialized, mediaInfo: MediaInfo = anImageInfo(), + canDownload: Boolean = true, + canShare: Boolean = true, ) = MediaViewerState( mediaInfo = mediaInfo, thumbnailSource = null, downloadedMedia = downloadedMedia, - snackbarMessage = null + snackbarMessage = null, + canDownload = canDownload, + canShare = canShare, ) {} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt similarity index 84% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt index 66cb0e6ba3..d7f743f7ad 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt @@ -16,7 +16,7 @@ @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.fadeIn @@ -47,11 +47,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp 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.core.mimetype.MimeTypes 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.matrix.api.media.MediaSource 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 kotlinx.coroutines.delay @@ -96,6 +96,8 @@ fun MediaViewerView( actionsEnabled = state.downloadedMedia is Async.Success, mimeType = state.mediaInfo.mimeType, onBackPressed = onBackPressed, + canDownload = state.canDownload, + canShare = state.canShare, eventSink = state.eventSink ) }, @@ -162,9 +164,12 @@ private fun rememberShowProgress(downloadedMedia: Async): Boolean { return showProgress } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun MediaViewerTopBar( actionsEnabled: Boolean, + canDownload: Boolean, + canShare: Boolean, mimeType: String, onBackPressed: () -> Unit, eventSink: (MediaViewerEvents) -> Unit, @@ -190,27 +195,31 @@ private fun MediaViewerTopBar( ) } } - IconButton( - enabled = actionsEnabled, - onClick = { - eventSink(MediaViewerEvents.SaveOnDisk) - }, - ) { - Icon( - resourceId = CompoundDrawables.ic_download, - contentDescription = stringResource(id = CommonStrings.action_save), - ) + if (canDownload) { + IconButton( + enabled = actionsEnabled, + onClick = { + eventSink(MediaViewerEvents.SaveOnDisk) + }, + ) { + Icon( + resourceId = CompoundDrawables.ic_download, + contentDescription = stringResource(id = CommonStrings.action_save), + ) + } } - IconButton( - enabled = actionsEnabled, - onClick = { - eventSink(MediaViewerEvents.Share) - }, - ) { - Icon( - resourceId = CompoundDrawables.ic_share_android, - contentDescription = stringResource(id = CommonStrings.action_share) - ) + if (canShare) { + IconButton( + enabled = actionsEnabled, + onClick = { + eventSink(MediaViewerEvents.Share) + }, + ) { + Icon( + resourceId = CompoundDrawables.ic_share_android, + contentDescription = stringResource(id = CommonStrings.action_share) + ) + } } } ) diff --git a/features/messages/impl/src/main/res/drawable/ic_apk_install.xml b/libraries/mediaviewer/api/src/main/res/drawable/ic_apk_install.xml similarity index 100% rename from features/messages/impl/src/main/res/drawable/ic_apk_install.xml rename to libraries/mediaviewer/api/src/main/res/drawable/ic_apk_install.xml diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt similarity index 90% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenterTest.kt rename to libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt index dfd648bd22..ce2b4651a5 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt @@ -16,20 +16,23 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.features.messages.impl.media.viewer +package io.element.android.libraries.mediaviewer import android.net.Uri import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test 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.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.matrix.test.media.FakeMediaLoader 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.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -148,12 +151,16 @@ class MediaViewerPresenterTest { mediaLoader: FakeMediaLoader, localMediaActions: FakeLocalMediaActions, snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(), + canShare: Boolean = true, + canDownload: Boolean = true, ): MediaViewerPresenter { return MediaViewerPresenter( inputs = MediaViewerNode.Inputs( mediaInfo = TESTED_MEDIA_INFO, mediaSource = aMediaSource(), - thumbnailSource = null + thumbnailSource = null, + canShare = canShare, + canDownload = canDownload, ), localMediaFactory = localMediaFactory, mediaLoader = mediaLoader, diff --git a/libraries/mediaviewer/impl/build.gradle.kts b/libraries/mediaviewer/impl/build.gradle.kts new file mode 100644 index 0000000000..7cfb9d01f4 --- /dev/null +++ b/libraries/mediaviewer/impl/build.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + id("io.element.android-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) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/local/AndroidLocalMediaActions.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/local/AndroidLocalMediaActions.kt index ef93574134..04d56a80d6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/local/AndroidLocalMediaActions.kt @@ -14,7 +14,7 @@ * 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.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.di.AppScope 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.withContext import timber.log.Timber diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/local/AndroidLocalMediaFactory.kt similarity index 88% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/local/AndroidLocalMediaFactory.kt index 4ed08edf6f..d258da4c88 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/local/AndroidLocalMediaFactory.kt @@ -14,13 +14,12 @@ * 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.net.Uri import androidx.core.net.toUri 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.getFileSize 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.matrix.api.media.MediaFile 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 @ContributesBinding(AppScope::class) diff --git a/libraries/mediaviewer/test/build.gradle.kts b/libraries/mediaviewer/test/build.gradle.kts new file mode 100644 index 0000000000..6bbedd7f1d --- /dev/null +++ b/libraries/mediaviewer/test/build.gradle.kts @@ -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) +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/FakeLocalMediaActions.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt similarity index 88% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/FakeLocalMediaActions.kt rename to libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt index 351bb6aeca..8c303de541 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/FakeLocalMediaActions.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt @@ -14,11 +14,11 @@ * 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 io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.LocalMediaActions +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaActions import io.element.android.tests.testutils.simulateLongTask class FakeLocalMediaActions : LocalMediaActions { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/FakeLocalMediaFactory.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt similarity index 76% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/FakeLocalMediaFactory.kt rename to libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt index 724c6e7f55..84f1d87613 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/media/FakeLocalMediaFactory.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media +package io.element.android.libraries.mediaviewer.test 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.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( private val localMediaUri: Uri, diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/media.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/media.kt new file mode 100644 index 0000000000..e19113c163 --- /dev/null +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/media.kt @@ -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 +) + diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 627d0eec43..b8aadaa52c 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -105,6 +105,7 @@ fun DependencyHandlerScope.allLibrariesImpl() { implementation(project(":libraries:cryptography:impl")) implementation(project(":libraries:voicerecorder:impl")) implementation(project(":libraries:mediaplayer:impl")) + implementation(project(":libraries:mediaviewer:impl")) } fun DependencyHandlerScope.allServicesImpl() { diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6b7dc973bb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a475687a9b1684159dba1a08f9737c4f49a19c4b7d49cbf4d0b698403fbc84c +size 394434 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png