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 fc3fba9c59..d092dbbb13 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 @@ -19,6 +19,7 @@ import com.bumble.appyx.core.node.node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -66,8 +67,8 @@ import io.element.android.libraries.matrix.api.room.joinedRoomMembers import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.ui.messages.LocalRoomMemberProfilesCache import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache -import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode +import io.element.android.libraries.mediaviewer.api.MediaInfo +import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.libraries.textcomposer.mentions.LocalMentionSpanTheme import io.element.android.libraries.textcomposer.mentions.MentionSpanTheme import io.element.android.services.analytics.api.AnalyticsService @@ -86,6 +87,7 @@ class MessagesFlowNode @AssistedInject constructor( private val showLocationEntryPoint: ShowLocationEntryPoint, private val createPollEntryPoint: CreatePollEntryPoint, private val elementCallEntryPoint: ElementCallEntryPoint, + private val mediaViewerEntryPoint: MediaViewerEntryPoint, private val analyticsService: AnalyticsService, private val room: MatrixRoom, private val roomMemberProfilesCache: RoomMemberProfilesCache, @@ -228,14 +230,22 @@ class MessagesFlowNode @AssistedInject constructor( createNode(buildContext, listOf(callback, inputs)) } is NavTarget.MediaViewer -> { - val inputs = MediaViewerNode.Inputs( + val params = MediaViewerEntryPoint.Params( mediaInfo = navTarget.mediaInfo, mediaSource = navTarget.mediaSource, thumbnailSource = navTarget.thumbnailSource, canDownload = true, canShare = true, ) - createNode(buildContext, listOf(inputs)) + val callback = object : MediaViewerEntryPoint.Callback { + override fun onDone() { + backstack.pop() + } + } + mediaViewerEntryPoint.nodeBuilder(this, buildContext) + .params(params) + .callback(callback) + .build() } is NavTarget.AttachmentPreview -> { val inputs = AttachmentsPreviewNode.Inputs(navTarget.attachment) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt index 2417d8346e..e89d9a5052 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt @@ -20,12 +20,14 @@ import io.element.android.features.messages.impl.attachments.Attachment 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.mediaviewer.api.local.LocalMediaRenderer @ContributesNode(RoomScope::class) class AttachmentsPreviewNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, presenterFactory: AttachmentsPreviewPresenter.Factory, + private val localMediaRenderer: LocalMediaRenderer, ) : Node(buildContext, plugins = plugins) { data class Inputs(val attachment: Attachment) : NodeInputs @@ -46,6 +48,7 @@ class AttachmentsPreviewNode @AssistedInject constructor( val state = presenter.present() AttachmentsPreviewView( state = state, + localMediaRenderer = localMediaRenderer, modifier = modifier ) } 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 f7e215fa22..1a52316735 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 @@ -10,12 +10,9 @@ 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.libraries.mediaviewer.api.MediaInfo +import io.element.android.libraries.mediaviewer.api.anImageMediaInfo 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.aVideoMediaInfo -import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo -import io.element.android.libraries.mediaviewer.api.local.anAudioMediaInfo -import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo import io.element.android.libraries.textcomposer.model.TextEditorState import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown @@ -23,9 +20,6 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider get() = sequenceOf( anAttachmentsPreviewState(), - anAttachmentsPreviewState(mediaInfo = aVideoMediaInfo()), - anAttachmentsPreviewState(mediaInfo = anAudioMediaInfo()), - anAttachmentsPreviewState(mediaInfo = anApkMediaInfo()), anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Processing), anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f)), anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"))), 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 2145092282..876f6f9bdf 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 @@ -8,6 +8,7 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.activity.compose.BackHandler +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.IntrinsicSize @@ -20,6 +21,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -34,20 +36,20 @@ 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.TopAppBar -import io.element.android.libraries.mediaviewer.api.local.LocalMediaView -import io.element.android.libraries.mediaviewer.api.local.rememberLocalMediaViewState +import io.element.android.libraries.designsystem.utils.CommonDrawables +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer import io.element.android.libraries.textcomposer.TextComposer import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.VoiceMessageState import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.wysiwyg.display.TextDisplay -import me.saket.telephoto.zoomable.ZoomSpec -import me.saket.telephoto.zoomable.rememberZoomableState @OptIn(ExperimentalMaterial3Api::class) @Composable fun AttachmentsPreviewView( state: AttachmentsPreviewState, + localMediaRenderer: LocalMediaRenderer, modifier: Modifier = Modifier, ) { fun postSendAttachment() { @@ -82,6 +84,7 @@ fun AttachmentsPreviewView( ) { AttachmentPreviewContent( state = state, + localMediaRenderer = localMediaRenderer, onSendClick = ::postSendAttachment, ) } @@ -129,6 +132,7 @@ private fun AttachmentSendStateView( @Composable private fun AttachmentPreviewContent( state: AttachmentsPreviewState, + localMediaRenderer: LocalMediaRenderer, onSendClick: () -> Unit, ) { Box( @@ -142,17 +146,7 @@ private fun AttachmentPreviewContent( ) { when (val attachment = state.attachment) { is Attachment.Media -> { - val localMediaViewState = rememberLocalMediaViewState( - zoomableState = rememberZoomableState( - zoomSpec = ZoomSpec(maxZoomFactor = 4f, preventOverOrUnderZoom = false) - ) - ) - LocalMediaView( - modifier = Modifier.fillMaxSize(), - localMedia = attachment.localMedia, - localMediaViewState = localMediaViewState, - onClick = {} - ) + localMediaRenderer.Render(attachment.localMedia) } } } @@ -205,5 +199,15 @@ private fun AttachmentsPreviewBottomActions( internal fun AttachmentsPreviewViewPreview(@PreviewParameter(AttachmentsPreviewStateProvider::class) state: AttachmentsPreviewState) = ElementPreviewDark { AttachmentsPreviewView( state = state, + localMediaRenderer = object : LocalMediaRenderer { + @Composable + override fun Render(localMedia: LocalMedia) { + Image( + painter = painterResource(id = CommonDrawables.sample_background), + modifier = Modifier.fillMaxSize(), + contentDescription = null, + ) + } + } ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt index 405d356edf..51c4cb43ba 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt @@ -35,7 +35,7 @@ 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.matrix.test.permalink.FakePermalinkParser -import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation +import io.element.android.libraries.mediaviewer.test.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/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt index 07ff31690e..b771141ce3 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 @@ -64,7 +64,7 @@ import io.element.android.libraries.matrix.test.media.aMediaSource import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.timeline.aStickerContent import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH -import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation +import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.test.runTest 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 4030e30272..166a22d694 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 @@ -15,6 +15,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -32,20 +33,16 @@ import io.element.android.features.roomdetails.impl.members.details.RoomMemberDe import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode import io.element.android.features.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode import io.element.android.features.userprofile.shared.UserProfileNodeHelper -import io.element.android.features.userprofile.shared.avatar.AvatarPreviewNode import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.overlay.operation.show -import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.RoomScope 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.permalink.PermalinkData import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode +import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction import kotlinx.parcelize.Parcelize @@ -59,6 +56,7 @@ class RoomDetailsFlowNode @AssistedInject constructor( private val room: MatrixRoom, private val analyticsService: AnalyticsService, private val messagesEntryPoint: MessagesEntryPoint, + private val mediaViewerEntryPoint: MediaViewerEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), @@ -202,22 +200,18 @@ class RoomDetailsFlowNode @AssistedInject constructor( createNode(buildContext, plugins) } is NavTarget.AvatarPreview -> { - // We need to fake the MimeType here for the viewer to work. - val mimeType = MimeTypes.Images - val input = MediaViewerNode.Inputs( - mediaInfo = MediaInfo( - filename = navTarget.name, - caption = null, - mimeType = mimeType, - formattedFileSize = "", - fileExtension = "" - ), - mediaSource = MediaSource(url = navTarget.avatarUrl), - thumbnailSource = null, - canDownload = false, - canShare = false, - ) - createNode(buildContext, listOf(input)) + val callback = object : MediaViewerEntryPoint.Callback { + override fun onDone() { + backstack.pop() + } + } + mediaViewerEntryPoint.nodeBuilder(this, buildContext) + .avatar( + navTarget.name, + navTarget.avatarUrl, + ) + .callback(callback) + .build() } is NavTarget.PollHistory -> { diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt index b544ad4750..ce0d4a07f0 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt @@ -15,6 +15,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -24,18 +25,14 @@ import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.features.userprofile.api.UserProfileEntryPoint import io.element.android.features.userprofile.impl.root.UserProfileNode import io.element.android.features.userprofile.shared.UserProfileNodeHelper -import io.element.android.features.userprofile.shared.avatar.AvatarPreviewNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder -import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode +import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) @@ -44,6 +41,7 @@ class UserProfileFlowNode @AssistedInject constructor( @Assisted plugins: List, private val elementCallEntryPoint: ElementCallEntryPoint, private val sessionIdHolder: CurrentSessionIdHolder, + private val mediaViewerEntryPoint: MediaViewerEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, @@ -80,22 +78,18 @@ class UserProfileFlowNode @AssistedInject constructor( createNode(buildContext, listOf(callback, params)) } is NavTarget.AvatarPreview -> { - // We need to fake the MimeType here for the viewer to work. - val mimeType = MimeTypes.Images - val input = MediaViewerNode.Inputs( - mediaInfo = MediaInfo( + val callback = object : MediaViewerEntryPoint.Callback { + override fun onDone() { + backstack.pop() + } + } + mediaViewerEntryPoint.nodeBuilder(this, buildContext) + .avatar( filename = navTarget.name, - caption = null, - mimeType = mimeType, - formattedFileSize = "", - fileExtension = "", - ), - mediaSource = MediaSource(url = navTarget.avatarUrl), - thumbnailSource = null, - canDownload = false, - canShare = false, - ) - createNode(buildContext, listOf(input)) + avatarUrl = navTarget.avatarUrl + ) + .callback(callback) + .build() } } } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/avatar/AvatarPreviewNode.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/avatar/AvatarPreviewNode.kt deleted file mode 100644 index 159b7f56f6..0000000000 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/avatar/AvatarPreviewNode.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.features.userprofile.shared.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.SessionScope -import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode -import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerPresenter - -@ContributesNode(SessionScope::class) -class AvatarPreviewNode @AssistedInject constructor( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - presenterFactory: MediaViewerPresenter.Factory, -) : MediaViewerNode(buildContext, plugins, presenterFactory) diff --git a/libraries/mediaviewer/api/build.gradle.kts b/libraries/mediaviewer/api/build.gradle.kts index 5d434ae094..d36a3605d2 100644 --- a/libraries/mediaviewer/api/build.gradle.kts +++ b/libraries/mediaviewer/api/build.gradle.kts @@ -13,45 +13,12 @@ plugins { android { namespace = "io.element.android.libraries.mediaviewer.api" - testOptions { - unitTests { - isIncludeAndroidResources = true - } - } } setupAnvil() dependencies { - 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(libs.telephoto.flick) - - implementation(projects.libraries.androidutils) - implementation(projects.libraries.architecture) implementation(projects.libraries.core) - implementation(projects.libraries.dateformatter.api) - implementation(projects.libraries.di) - implementation(projects.libraries.designsystem) + implementation(projects.libraries.architecture) 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.robolectric) - testImplementation(libs.test.turbine) - testImplementation(libs.coroutines.core) - testImplementation(libs.coroutines.test) - testImplementation(libs.androidx.compose.ui.test.junit) - testReleaseImplementation(libs.androidx.compose.ui.test.manifest) } diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt similarity index 93% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt index 5ded65b2b3..93cd2f9378 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt @@ -1,11 +1,11 @@ /* - * Copyright 2023, 2024 New Vector Ltd. + * Copyright 2024 New Vector Ltd. * * SPDX-License-Identifier: AGPL-3.0-only * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local +package io.element.android.libraries.mediaviewer.api import android.os.Parcelable import io.element.android.libraries.core.mimetype.MimeTypes diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt new file mode 100644 index 0000000000..fb5ee5dece --- /dev/null +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.api + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.matrix.api.media.MediaSource + +interface MediaViewerEntryPoint : FeatureEntryPoint { + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + + interface NodeBuilder { + fun callback(callback: Callback): NodeBuilder + fun params(params: Params): NodeBuilder + fun avatar(filename: String, avatarUrl: String): NodeBuilder + fun build(): Node + } + + interface Callback : Plugin { + fun onDone() + } + + data class Params( + val mediaInfo: MediaInfo, + val mediaSource: MediaSource, + val thumbnailSource: MediaSource?, + val canDownload: Boolean, + val canShare: Boolean, + ) : NodeInputs +} diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt index 56bda259a6..fade670697 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.mediaviewer.api.local import android.net.Uri import android.os.Parcelable import androidx.compose.runtime.Immutable +import io.element.android.libraries.mediaviewer.api.MediaInfo import kotlinx.parcelize.Parcelize @Parcelize diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt index 9200070263..beeccb7c48 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.mediaviewer.api.local import android.net.Uri import io.element.android.libraries.matrix.api.media.MediaFile +import io.element.android.libraries.mediaviewer.api.MediaInfo interface LocalMediaFactory { /** diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaRenderer.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaRenderer.kt new file mode 100644 index 0000000000..3b387ef0d7 --- /dev/null +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaRenderer.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.api.local + +import androidx.compose.runtime.Composable + +interface LocalMediaRenderer { + @Composable + fun Render(localMedia: LocalMedia) +} diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt index 32ca151160..e7586a6f87 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt @@ -7,30 +7,6 @@ package io.element.android.libraries.mediaviewer.api.util -import android.webkit.MimeTypeMap -import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.libraries.di.AppScope -import javax.inject.Inject - interface FileExtensionExtractor { fun extractFromName(name: String): String } - -@ContributesBinding(AppScope::class) -class FileExtensionExtractorWithValidation @Inject constructor() : FileExtensionExtractor { - override fun extractFromName(name: String): String { - val fileExtension = name.substringAfterLast('.', "") - // Makes sure the extension is known by the system, otherwise default to binary extension. - return if (MimeTypeMap.getSingleton().hasExtension(fileExtension)) { - fileExtension - } else { - "bin" - } - } -} - -class FileExtensionExtractorWithoutValidation : FileExtensionExtractor { - override fun extractFromName(name: String): String { - return name.substringAfterLast('.', "") - } -} diff --git a/libraries/mediaviewer/impl/build.gradle.kts b/libraries/mediaviewer/impl/build.gradle.kts index a814f64ab7..5ebc252343 100644 --- a/libraries/mediaviewer/impl/build.gradle.kts +++ b/libraries/mediaviewer/impl/build.gradle.kts @@ -13,6 +13,11 @@ plugins { android { namespace = "io.element.android.libraries.mediaviewer.impl" + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } } setupAnvil() @@ -21,6 +26,23 @@ dependencies { implementation(libs.coroutines.core) implementation(libs.dagger) + implementation(libs.coil.compose) + implementation(libs.androidx.media3.exoplayer) + implementation(libs.androidx.media3.ui) + implementation(libs.telephoto.zoomableimage) + implementation(libs.vanniktech.blurhash) + implementation(libs.telephoto.flick) + + implementation(projects.libraries.androidutils) + implementation(projects.libraries.architecture) + implementation(projects.libraries.core) + implementation(projects.libraries.dateformatter.api) + implementation(projects.libraries.di) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + implementation(projects.libraries.uiStrings) + api(projects.libraries.mediaviewer.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) @@ -39,4 +61,6 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(libs.coroutines.core) testImplementation(libs.coroutines.test) + testImplementation(libs.androidx.compose.ui.test.junit) + testReleaseImplementation(libs.androidx.compose.ui.test.manifest) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt new file mode 100644 index 0000000000..5bd16c840b --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.mediaviewer.api.MediaInfo +import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint +import io.element.android.libraries.mediaviewer.impl.viewer.MediaViewerNode +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultMediaViewerEntryPoint @Inject constructor() : MediaViewerEntryPoint { + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MediaViewerEntryPoint.NodeBuilder { + val plugins = ArrayList() + + return object : MediaViewerEntryPoint.NodeBuilder { + override fun callback(callback: MediaViewerEntryPoint.Callback): MediaViewerEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun params(params: MediaViewerEntryPoint.Params): MediaViewerEntryPoint.NodeBuilder { + plugins += params + return this + } + + override fun avatar(filename: String, avatarUrl: String): MediaViewerEntryPoint.NodeBuilder { + // We need to fake the MimeType here for the viewer to work. + val mimeType = MimeTypes.Images + return params( + MediaViewerEntryPoint.Params( + mediaInfo = MediaInfo( + filename = filename, + caption = null, + mimeType = mimeType, + formattedFileSize = "", + fileExtension = "" + ), + mediaSource = MediaSource(url = avatarUrl), + thumbnailSource = null, + canDownload = false, + canShare = false, + ) + ) + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } + } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt index 66bed465ed..7bd92a800f 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt @@ -35,7 +35,6 @@ 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/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt index 06fefa2623..461563f465 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt @@ -20,9 +20,9 @@ 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.MediaInfo 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 diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt new file mode 100644 index 0000000000..6b1005dccf --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.local + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer +import me.saket.telephoto.zoomable.ZoomSpec +import me.saket.telephoto.zoomable.rememberZoomableState +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultLocalMediaRenderer @Inject constructor() : LocalMediaRenderer { + @Composable + override fun Render(localMedia: LocalMedia) { + val localMediaViewState = rememberLocalMediaViewState( + zoomableState = rememberZoomableState( + zoomSpec = ZoomSpec(maxZoomFactor = 4f, preventOverOrUnderZoom = false) + ) + ) + LocalMediaView( + modifier = Modifier.fillMaxSize(), + localMedia = localMedia, + localMediaViewState = localMediaViewState, + onClick = {} + ) + } +} diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaActions.kt similarity index 87% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaActions.kt index c7f0ba7d90..92f873e2ea 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaActions.kt @@ -5,9 +5,10 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local +package io.element.android.libraries.mediaviewer.impl.local import androidx.compose.runtime.Composable +import io.element.android.libraries.mediaviewer.api.local.LocalMedia interface LocalMediaActions { @Composable diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt similarity index 91% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt index b7102b0f84..be48711bf0 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local +package io.element.android.libraries.mediaviewer.impl.local import android.annotation.SuppressLint import android.net.Uri @@ -67,12 +67,14 @@ 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.MediaInfo import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize -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.mediaviewer.api.player.MediaPlayerControllerState -import io.element.android.libraries.mediaviewer.api.player.MediaPlayerControllerView +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.impl.local.exoplayer.ExoPlayerWrapper +import io.element.android.libraries.mediaviewer.impl.local.pdf.PdfViewer +import io.element.android.libraries.mediaviewer.impl.local.pdf.rememberPdfViewerState +import io.element.android.libraries.mediaviewer.impl.player.MediaPlayerControllerState +import io.element.android.libraries.mediaviewer.impl.player.MediaPlayerControllerView import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.delay import me.saket.telephoto.zoomable.coil.ZoomableAsyncImage @@ -153,8 +155,8 @@ private fun MediaVideoView( if (LocalInspectionMode.current) { Text( modifier = modifier - .background(ElementTheme.colors.bgSubtlePrimary) - .wrapContentSize(), + .background(ElementTheme.colors.bgSubtlePrimary) + .wrapContentSize(), text = "A Video Player will render here", ) } else { @@ -267,8 +269,8 @@ private fun ExoPlayerMediaVideoView( KeepScreenOn(mediaPlayerControllerState.isPlaying) Box( modifier = modifier - .background(ElementTheme.colors.bgSubtlePrimary) - .wrapContentSize(), + .background(ElementTheme.colors.bgSubtlePrimary) + .wrapContentSize(), ) { AndroidView( modifier = Modifier.fillMaxSize(), @@ -318,8 +320,8 @@ private fun ExoPlayerMediaVideoView( exoPlayer.volume = if (exoPlayer.volume == 1f) 0f else 1f }, modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter), + .fillMaxWidth() + .align(Alignment.BottomCenter), ) } @@ -369,20 +371,20 @@ private fun MediaFileView( val interactionSource = remember { MutableInteractionSource() } Box( modifier = modifier - .padding(horizontal = 8.dp) - .clickable( - onClick = onClick, - interactionSource = interactionSource, - indication = null - ), + .padding(horizontal = 8.dp) + .clickable( + onClick = onClick, + interactionSource = interactionSource, + indication = null + ), contentAlignment = Alignment.Center ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Box( modifier = Modifier - .size(72.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.onBackground), + .size(72.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.onBackground), contentAlignment = Alignment.Center, ) { Icon( @@ -390,8 +392,8 @@ private fun MediaFileView( contentDescription = null, tint = MaterialTheme.colorScheme.background, modifier = Modifier - .size(32.dp) - .rotate(if (isAudio) 0f else -45f), + .size(32.dp) + .rotate(if (isAudio) 0f else -45f), ) } if (info != null) { diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaViewState.kt similarity index 95% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaViewState.kt index b7237c26eb..8705ef49d3 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaViewState.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local +package io.element.android.libraries.mediaviewer.impl.local import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/exoplayer/ExoPlayerWrapper.kt similarity index 93% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/exoplayer/ExoPlayerWrapper.kt index 740f1675e0..41d59b395b 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/exoplayer/ExoPlayerWrapper.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local.exoplayer +package io.element.android.libraries.mediaviewer.impl.local.exoplayer import android.content.Context import androidx.media3.common.Player diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/ParcelFileDescriptorFactory.kt similarity index 91% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/ParcelFileDescriptorFactory.kt index e73f24ea61..bd931cadcb 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/ParcelFileDescriptorFactory.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local.pdf +package io.element.android.libraries.mediaviewer.impl.local.pdf import android.content.Context import android.net.Uri diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfPage.kt similarity index 98% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfPage.kt index 137c3e321f..655d577fda 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfPage.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local.pdf +package io.element.android.libraries.mediaviewer.impl.local.pdf import android.graphics.Bitmap import android.graphics.Canvas diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfRendererManager.kt similarity index 97% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfRendererManager.kt index 255822b853..02f5ef58bf 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfRendererManager.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local.pdf +package io.element.android.libraries.mediaviewer.impl.local.pdf import android.graphics.pdf.PdfRenderer import android.os.ParcelFileDescriptor diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewer.kt similarity index 98% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewer.kt index 6202db88b1..ffb8e83d45 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewer.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local.pdf +package io.element.android.libraries.mediaviewer.impl.local.pdf import androidx.compose.foundation.Image import androidx.compose.foundation.background diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewerState.kt similarity index 97% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewerState.kt index 72eb73c301..095d4d15c4 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewerState.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.local.pdf +package io.element.android.libraries.mediaviewer.impl.local.pdf import android.content.Context import androidx.compose.foundation.lazy.LazyListState diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerState.kt similarity index 84% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerState.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerState.kt index f5197af99b..d922483ff1 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerState.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.player +package io.element.android.libraries.mediaviewer.impl.player data class MediaPlayerControllerState( val isVisible: Boolean, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerStateProvider.kt similarity index 94% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerStateProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerStateProvider.kt index 5cffb63990..6aa93547f6 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerStateProvider.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.player +package io.element.android.libraries.mediaviewer.impl.player import androidx.compose.ui.tooling.preview.PreviewParameterProvider diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerView.kt similarity index 99% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerView.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerView.kt index 188d4e0c4a..c9f548aab2 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/player/MediaPlayerControllerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/player/MediaPlayerControllerView.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.player +package io.element.android.libraries.mediaviewer.impl.player import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/util/FileExtensionExtractor.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/util/FileExtensionExtractor.kt new file mode 100644 index 0000000000..026be26b2e --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/util/FileExtensionExtractor.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.util + +import android.webkit.MimeTypeMap +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class FileExtensionExtractorWithValidation @Inject constructor() : FileExtensionExtractor { + override fun extractFromName(name: String): String { + val fileExtension = name.substringAfterLast('.', "") + // Makes sure the extension is known by the system, otherwise default to binary extension. + return if (MimeTypeMap.getSingleton().hasExtension(fileExtension)) { + fileExtension + } else { + "bin" + } + } +} diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvents.kt similarity index 87% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvents.kt index f72becb58f..ac2714584c 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvents.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.viewer +package io.element.android.libraries.mediaviewer.impl.viewer sealed interface MediaViewerEvents { data object SaveOnDisk : MediaViewerEvents diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt similarity index 69% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt index 7d173f5291..83c7c1aca7 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt @@ -5,22 +5,21 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.viewer +package io.element.android.libraries.mediaviewer.impl.viewer import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins 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.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 +import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint @ContributesNode(RoomScope::class) open class MediaViewerNode @AssistedInject constructor( @@ -28,15 +27,13 @@ open class MediaViewerNode @AssistedInject constructor( @Assisted plugins: List, presenterFactory: MediaViewerPresenter.Factory, ) : Node(buildContext, plugins = plugins) { - data class Inputs( - val mediaInfo: MediaInfo, - val mediaSource: MediaSource, - val thumbnailSource: MediaSource?, - val canDownload: Boolean, - val canShare: Boolean, - ) : NodeInputs + private val inputs = inputs() - private val inputs: Inputs = inputs() + private fun onDone() { + plugins().forEach { + it.onDone() + } + } private val presenter = presenterFactory.create(inputs) @@ -47,7 +44,7 @@ open class MediaViewerNode @AssistedInject constructor( MediaViewerView( state = state, modifier = modifier, - onBackClick = this::navigateUp + onBackClick = ::onDone ) } } diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt similarity index 94% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index 3cef900040..068fb02b0f 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.viewer +package io.element.android.libraries.mediaviewer.impl.viewer import android.content.ActivityNotFoundException import androidx.compose.runtime.Composable @@ -27,16 +27,17 @@ 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.MediaViewerEntryPoint 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.mediaviewer.impl.local.LocalMediaActions import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import io.element.android.libraries.androidutils.R as UtilsR class MediaViewerPresenter @AssistedInject constructor( - @Assisted private val inputs: MediaViewerNode.Inputs, + @Assisted private val inputs: MediaViewerEntryPoint.Params, private val localMediaFactory: LocalMediaFactory, private val mediaLoader: MatrixMediaLoader, private val localMediaActions: LocalMediaActions, @@ -44,7 +45,7 @@ class MediaViewerPresenter @AssistedInject constructor( ) : Presenter { @AssistedFactory interface Factory { - fun create(inputs: MediaViewerNode.Inputs): MediaViewerPresenter + fun create(inputs: MediaViewerEntryPoint.Params): MediaViewerPresenter } @Composable diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt similarity index 85% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt index 3dd9f1b372..94d6653241 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt @@ -5,13 +5,13 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.viewer +package io.element.android.libraries.mediaviewer.impl.viewer import io.element.android.libraries.architecture.AsyncData 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.MediaInfo import io.element.android.libraries.mediaviewer.api.local.LocalMedia -import io.element.android.libraries.mediaviewer.api.local.MediaInfo data class MediaViewerState( val mediaInfo: MediaInfo, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt similarity index 84% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt index 2ae215ac96..4ffcb75b07 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt @@ -5,18 +5,18 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.viewer +package io.element.android.libraries.mediaviewer.impl.viewer import android.net.Uri import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.mediaviewer.api.MediaInfo +import io.element.android.libraries.mediaviewer.api.aPdfMediaInfo +import io.element.android.libraries.mediaviewer.api.aVideoMediaInfo +import io.element.android.libraries.mediaviewer.api.anApkMediaInfo +import io.element.android.libraries.mediaviewer.api.anAudioMediaInfo +import io.element.android.libraries.mediaviewer.api.anImageMediaInfo 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.aPdfMediaInfo -import io.element.android.libraries.mediaviewer.api.local.aVideoMediaInfo -import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo -import io.element.android.libraries.mediaviewer.api.local.anAudioMediaInfo -import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo open class MediaViewerStateProvider : PreviewParameterProvider { override val values: Sequence diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt similarity index 95% rename from libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 9f362c13fa..ae5e9401cb 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -7,8 +7,9 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package io.element.android.libraries.mediaviewer.api.viewer +package io.element.android.libraries.mediaviewer.impl.viewer +import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn @@ -55,12 +56,12 @@ 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.MediaInfo 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.PlayableState -import io.element.android.libraries.mediaviewer.api.local.rememberLocalMediaViewState +import io.element.android.libraries.mediaviewer.impl.R +import io.element.android.libraries.mediaviewer.impl.local.LocalMediaView +import io.element.android.libraries.mediaviewer.impl.local.PlayableState +import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaViewState import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.delay import me.saket.telephoto.flick.FlickToDismiss @@ -79,6 +80,7 @@ fun MediaViewerView( val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage) var showOverlay by remember { mutableStateOf(true) } + BackHandler { onBackClick() } Scaffold( modifier, containerColor = Color.Transparent, @@ -146,8 +148,8 @@ private fun MediaViewerPage( Box( modifier = Modifier - .fillMaxSize() - .navigationBarsPadding() + .fillMaxSize() + .navigationBarsPadding() ) { Box(contentAlignment = Alignment.Center) { val zoomableState = rememberZoomableState( @@ -191,8 +193,8 @@ private fun MediaViewerPage( if (showProgress) { LinearProgressIndicator( modifier = Modifier - .fillMaxWidth() - .height(2.dp) + .fillMaxWidth() + .height(2.dp) ) } } diff --git a/libraries/mediaviewer/api/src/main/res/drawable/ic_apk_install.xml b/libraries/mediaviewer/impl/src/main/res/drawable/ic_apk_install.xml similarity index 100% rename from libraries/mediaviewer/api/src/main/res/drawable/ic_apk_install.xml rename to libraries/mediaviewer/impl/src/main/res/drawable/ic_apk_install.xml diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt index eb78517a36..5729c5310d 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt @@ -12,9 +12,9 @@ import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.matrix.test.media.FakeMediaFile -import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo -import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation +import io.element.android.libraries.mediaviewer.api.MediaInfo +import io.element.android.libraries.mediaviewer.api.anImageMediaInfo +import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner diff --git a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractorTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/util/FileExtensionExtractorTest.kt similarity index 88% rename from libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractorTest.kt rename to libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/util/FileExtensionExtractorTest.kt index a9ab18ce61..5e9ada9f3a 100644 --- a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractorTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/util/FileExtensionExtractorTest.kt @@ -5,9 +5,10 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.util +package io.element.android.libraries.mediaviewer.impl.util import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner diff --git a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt similarity index 94% rename from libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt rename to libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt index a54ccf2cc2..cbe334216c 100644 --- a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt @@ -7,7 +7,7 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.libraries.mediaviewer +package io.element.android.libraries.mediaviewer.impl.viewer import android.net.Uri import app.cash.molecule.RecompositionMode @@ -18,10 +18,8 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.matrix.test.media.FakeMatrixMediaLoader import io.element.android.libraries.matrix.test.media.aMediaSource -import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo -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.libraries.mediaviewer.api.MediaViewerEntryPoint +import io.element.android.libraries.mediaviewer.api.anApkMediaInfo import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory import io.element.android.tests.testutils.WarmUpRule @@ -144,7 +142,7 @@ class MediaViewerPresenterTest { canDownload: Boolean = true, ): MediaViewerPresenter { return MediaViewerPresenter( - inputs = MediaViewerNode.Inputs( + inputs = MediaViewerEntryPoint.Params( mediaInfo = TESTED_MEDIA_INFO, mediaSource = aMediaSource(), thumbnailSource = null, diff --git a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt similarity index 97% rename from libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt rename to libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt index ee5aef8e2e..acbfb57619 100644 --- a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.libraries.mediaviewer.api.viewer +package io.element.android.libraries.mediaviewer.impl.viewer import android.net.Uri import androidx.activity.ComponentActivity @@ -18,8 +18,8 @@ import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeDown import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.mediaviewer.api.anImageMediaInfo import io.element.android.libraries.mediaviewer.api.local.LocalMedia -import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EventsRecorder diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt index b6437c212b..c8555e5bce 100644 --- a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt @@ -9,7 +9,7 @@ package io.element.android.libraries.mediaviewer.test import androidx.compose.runtime.Composable import io.element.android.libraries.mediaviewer.api.local.LocalMedia -import io.element.android.libraries.mediaviewer.api.local.LocalMediaActions +import io.element.android.libraries.mediaviewer.impl.local.LocalMediaActions import io.element.android.tests.testutils.simulateLongTask class FakeLocalMediaActions : LocalMediaActions { diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt index 43474e24bb..df85f2d53d 100644 --- a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt @@ -10,11 +10,11 @@ package io.element.android.libraries.mediaviewer.test import android.net.Uri import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaFile +import io.element.android.libraries.mediaviewer.api.MediaInfo 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 io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation +import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia class FakeLocalMediaFactory( diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/util/FileExtensionExtractorWithoutValidation.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/util/FileExtensionExtractorWithoutValidation.kt new file mode 100644 index 0000000000..c252c216aa --- /dev/null +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/util/FileExtensionExtractorWithoutValidation.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.test.util + +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor + +class FileExtensionExtractorWithoutValidation : FileExtensionExtractor { + override fun extractFromName(name: String): String { + return name.substringAfterLast('.', "") + } +} diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/LocalMedia.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/LocalMedia.kt index ea2953f69c..4f933b1c00 100644 --- a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/LocalMedia.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/LocalMedia.kt @@ -8,9 +8,9 @@ package io.element.android.libraries.mediaviewer.test.viewer import android.net.Uri +import io.element.android.libraries.mediaviewer.api.MediaInfo +import io.element.android.libraries.mediaviewer.api.anImageMediaInfo 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.anImageMediaInfo fun aLocalMedia( uri: Uri,