mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:55:41 +00:00
+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,
|
||||||
|
|||||||
+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(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user