mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Merge pull request #6933 from element-hq/feature/bma/filePreviewImprovement
[Media viewer] File preview improvement
This commit is contained in:
+24
@@ -103,6 +103,30 @@ fun aPdfMediaInfo(
|
|||||||
duration = null,
|
duration = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun aZipMediaInfo(
|
||||||
|
filename: String = "a zip file.zip",
|
||||||
|
caption: String? = null,
|
||||||
|
formattedCaption: CharSequence? = null,
|
||||||
|
senderName: String? = null,
|
||||||
|
dateSent: String? = null,
|
||||||
|
dateSentFull: String? = null,
|
||||||
|
): MediaInfo = MediaInfo(
|
||||||
|
filename = filename,
|
||||||
|
fileSize = 12 * 1024 * 1024,
|
||||||
|
caption = caption,
|
||||||
|
formattedCaption = formattedCaption,
|
||||||
|
mimeType = MimeTypes.Pdf,
|
||||||
|
formattedFileSize = "45MB",
|
||||||
|
fileExtension = "zip",
|
||||||
|
senderId = UserId("@alice:server.org"),
|
||||||
|
senderName = senderName,
|
||||||
|
senderAvatar = null,
|
||||||
|
dateSent = dateSent,
|
||||||
|
dateSentFull = dateSentFull,
|
||||||
|
waveform = null,
|
||||||
|
duration = null,
|
||||||
|
)
|
||||||
|
|
||||||
fun anApkMediaInfo(
|
fun anApkMediaInfo(
|
||||||
senderId: UserId? = UserId("@alice:server.org"),
|
senderId: UserId? = UserId("@alice:server.org"),
|
||||||
senderName: String? = null,
|
senderName: String? = null,
|
||||||
|
|||||||
+2
-2
@@ -12,8 +12,8 @@ fun formatFileExtensionAndSize(extension: String, size: String?): String {
|
|||||||
return buildString {
|
return buildString {
|
||||||
append(extension.uppercase())
|
append(extension.uppercase())
|
||||||
if (size != null) {
|
if (size != null) {
|
||||||
append(' ')
|
append(" • ")
|
||||||
append("($size)")
|
append(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -41,6 +41,7 @@ class DefaultLocalMediaRenderer(
|
|||||||
textFileViewer = textFileViewer,
|
textFileViewer = textFileViewer,
|
||||||
audioFocus = audioFocus,
|
audioFocus = audioFocus,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
onOpenWith = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -31,6 +31,7 @@ fun LocalMediaView(
|
|||||||
bottomPaddingInPixels: Int,
|
bottomPaddingInPixels: Int,
|
||||||
audioFocus: AudioFocus?,
|
audioFocus: AudioFocus?,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
onOpenWith: (() -> Unit)?,
|
||||||
textFileViewer: TextFileViewer,
|
textFileViewer: TextFileViewer,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
isDisplayed: Boolean = true,
|
isDisplayed: Boolean = true,
|
||||||
@@ -80,7 +81,7 @@ fun LocalMediaView(
|
|||||||
uri = localMedia?.uri,
|
uri = localMedia?.uri,
|
||||||
info = mediaInfo,
|
info = mediaInfo,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onClick = onClick,
|
onOpenWith = onOpenWith,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-34
@@ -9,23 +9,16 @@
|
|||||||
package io.element.android.libraries.mediaviewer.impl.local.file
|
package io.element.android.libraries.mediaviewer.impl.local.file
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.draw.rotate
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
@@ -33,55 +26,54 @@ import androidx.compose.ui.unit.dp
|
|||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.libraries.core.bool.orFalse
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio
|
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio
|
||||||
|
import io.element.android.libraries.designsystem.components.BigIcon
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.mediaviewer.api.MediaInfo
|
import io.element.android.libraries.mediaviewer.api.MediaInfo
|
||||||
import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize
|
import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize
|
||||||
|
import io.element.android.libraries.mediaviewer.impl.R
|
||||||
import io.element.android.libraries.mediaviewer.impl.local.LocalMediaViewState
|
import io.element.android.libraries.mediaviewer.impl.local.LocalMediaViewState
|
||||||
import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaViewState
|
import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaViewState
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=3361-16623
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun MediaFileView(
|
fun MediaFileView(
|
||||||
localMediaViewState: LocalMediaViewState,
|
localMediaViewState: LocalMediaViewState,
|
||||||
uri: Uri?,
|
uri: Uri?,
|
||||||
info: MediaInfo?,
|
info: MediaInfo?,
|
||||||
onClick: () -> Unit,
|
onOpenWith: (() -> Unit)?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val isAudio = info?.mimeType.isMimeTypeAudio().orFalse()
|
val isAudio = info?.mimeType.isMimeTypeAudio().orFalse()
|
||||||
localMediaViewState.isReady = uri != null
|
localMediaViewState.isReady = uri != null
|
||||||
|
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.padding(horizontal = 8.dp)
|
.padding(horizontal = 8.dp),
|
||||||
.clickable(
|
|
||||||
onClick = onClick,
|
|
||||||
interactionSource = interactionSource,
|
|
||||||
indication = null
|
|
||||||
),
|
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Box(
|
val isApk = info?.mimeType == MimeTypes.Apk
|
||||||
modifier = Modifier
|
val icon = when {
|
||||||
.size(72.dp)
|
isAudio -> CompoundIcons.Audio()
|
||||||
.clip(CircleShape)
|
isApk -> CompoundIcons.Bug()
|
||||||
.background(ElementTheme.colors.iconPrimary),
|
else -> CompoundIcons.Files()
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = if (isAudio) CompoundIcons.Audio() else CompoundIcons.Attachment(),
|
|
||||||
contentDescription = null,
|
|
||||||
tint = ElementTheme.colors.iconOnSolidPrimary,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(32.dp)
|
|
||||||
.rotate(if (isAudio) 0f else -45f),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
BigIcon(
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
|
style = BigIcon.Style.Default(
|
||||||
|
vectorIcon = icon,
|
||||||
|
usePrimaryTint = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
Text(
|
Text(
|
||||||
@@ -100,6 +92,20 @@ fun MediaFileView(
|
|||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = ElementTheme.colors.textPrimary
|
color = ElementTheme.colors.textPrimary
|
||||||
)
|
)
|
||||||
|
if (onOpenWith != null) {
|
||||||
|
val (icon, textResId) = if (isApk) {
|
||||||
|
IconSource.Resource(R.drawable.ic_apk_install) to CommonStrings.common_install_apk_android
|
||||||
|
} else {
|
||||||
|
IconSource.Vector(CompoundIcons.PopOut()) to CommonStrings.action_open_with
|
||||||
|
}
|
||||||
|
OutlinedButton(
|
||||||
|
modifier = Modifier.padding(top = 24.dp),
|
||||||
|
size = ButtonSize.Small,
|
||||||
|
leadingIcon = icon,
|
||||||
|
onClick = onOpenWith,
|
||||||
|
text = stringResource(id = textResId),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,6 +121,6 @@ internal fun MediaFileViewPreview(
|
|||||||
localMediaViewState = rememberLocalMediaViewState(),
|
localMediaViewState = rememberLocalMediaViewState(),
|
||||||
uri = null,
|
uri = null,
|
||||||
info = info,
|
info = info,
|
||||||
onClick = {},
|
onOpenWith = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -10,11 +10,15 @@ package io.element.android.libraries.mediaviewer.impl.local.file
|
|||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.mediaviewer.api.MediaInfo
|
import io.element.android.libraries.mediaviewer.api.MediaInfo
|
||||||
import io.element.android.libraries.mediaviewer.api.aPdfMediaInfo
|
import io.element.android.libraries.mediaviewer.api.aZipMediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.anApkMediaInfo
|
||||||
|
import io.element.android.libraries.mediaviewer.api.anAudioMediaInfo
|
||||||
|
|
||||||
open class MediaInfoFileProvider : PreviewParameterProvider<MediaInfo> {
|
open class MediaInfoFileProvider : PreviewParameterProvider<MediaInfo> {
|
||||||
override val values: Sequence<MediaInfo>
|
override val values: Sequence<MediaInfo>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aPdfMediaInfo(),
|
aZipMediaInfo(),
|
||||||
|
anAudioMediaInfo(),
|
||||||
|
anApkMediaInfo(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -106,7 +106,7 @@ import me.saket.telephoto.zoomable.OverzoomEffect
|
|||||||
import me.saket.telephoto.zoomable.ZoomSpec
|
import me.saket.telephoto.zoomable.ZoomSpec
|
||||||
import me.saket.telephoto.zoomable.rememberZoomableState
|
import me.saket.telephoto.zoomable.rememberZoomableState
|
||||||
|
|
||||||
val topAppBarHeight = 88.dp
|
val topAppBarHeight = 112.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=3361-16623
|
* Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=3361-16623
|
||||||
@@ -239,6 +239,9 @@ fun MediaViewerView(
|
|||||||
onRetry = {
|
onRetry = {
|
||||||
state.eventSink(MediaViewerEvent.LoadMedia(dataForPage))
|
state.eventSink(MediaViewerEvent.LoadMedia(dataForPage))
|
||||||
},
|
},
|
||||||
|
onOpenWith = {
|
||||||
|
state.eventSink(MediaViewerEvent.OpenWith(dataForPage))
|
||||||
|
},
|
||||||
onDismissError = {
|
onDismissError = {
|
||||||
state.eventSink(MediaViewerEvent.ClearLoadingError(dataForPage))
|
state.eventSink(MediaViewerEvent.ClearLoadingError(dataForPage))
|
||||||
},
|
},
|
||||||
@@ -333,6 +336,7 @@ private fun MediaViewerPage(
|
|||||||
isUserSelected: Boolean,
|
isUserSelected: Boolean,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onRetry: () -> Unit,
|
onRetry: () -> Unit,
|
||||||
|
onOpenWith: () -> Unit,
|
||||||
onDismissError: () -> Unit,
|
onDismissError: () -> Unit,
|
||||||
onShowOverlayChange: (Boolean) -> Unit,
|
onShowOverlayChange: (Boolean) -> Unit,
|
||||||
audioFocus: AudioFocus?,
|
audioFocus: AudioFocus?,
|
||||||
@@ -387,6 +391,7 @@ private fun MediaViewerPage(
|
|||||||
currentOnShowOverlayChange(!currentShowOverlay)
|
currentOnShowOverlayChange(!currentShowOverlay)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onOpenWith = onOpenWith,
|
||||||
isUserSelected = isUserSelected,
|
isUserSelected = isUserSelected,
|
||||||
audioFocus = audioFocus,
|
audioFocus = audioFocus,
|
||||||
)
|
)
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:43ab4873fc5fc812bf18af50cbe620c83b273ee70305b5ea06a7aeabdf8dbc93
|
oid sha256:58e3353ce8aecff415158d146347206b1dbf167d15523301847f47c2e81b1c5b
|
||||||
size 137637
|
size 137215
|
||||||
|
|||||||
LFS
BIN
Binary file not shown.
Reference in New Issue
Block a user