|
|
@ -34,7 +34,11 @@ import androidx.compose.material.icons.outlined.Attachment |
|
|
|
import androidx.compose.material3.MaterialTheme |
|
|
|
import androidx.compose.material3.MaterialTheme |
|
|
|
import androidx.compose.runtime.Composable |
|
|
|
import androidx.compose.runtime.Composable |
|
|
|
import androidx.compose.runtime.LaunchedEffect |
|
|
|
import androidx.compose.runtime.LaunchedEffect |
|
|
|
|
|
|
|
import androidx.compose.runtime.Stable |
|
|
|
|
|
|
|
import androidx.compose.runtime.getValue |
|
|
|
|
|
|
|
import androidx.compose.runtime.mutableStateOf |
|
|
|
import androidx.compose.runtime.remember |
|
|
|
import androidx.compose.runtime.remember |
|
|
|
|
|
|
|
import androidx.compose.runtime.setValue |
|
|
|
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.draw.clip |
|
|
@ -70,40 +74,52 @@ import me.saket.telephoto.zoomable.coil.ZoomableAsyncImage |
|
|
|
import me.saket.telephoto.zoomable.rememberZoomableImageState |
|
|
|
import me.saket.telephoto.zoomable.rememberZoomableImageState |
|
|
|
import me.saket.telephoto.zoomable.rememberZoomableState |
|
|
|
import me.saket.telephoto.zoomable.rememberZoomableState |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Stable |
|
|
|
|
|
|
|
class LocalMediaViewState { |
|
|
|
|
|
|
|
var isReady: Boolean by mutableStateOf(false) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Composable |
|
|
|
|
|
|
|
fun rememberLocalMediaViewState(): LocalMediaViewState { |
|
|
|
|
|
|
|
return remember { |
|
|
|
|
|
|
|
LocalMediaViewState() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressLint("UnsafeOptInUsageError") |
|
|
|
@SuppressLint("UnsafeOptInUsageError") |
|
|
|
@Composable |
|
|
|
@Composable |
|
|
|
fun LocalMediaView( |
|
|
|
fun LocalMediaView( |
|
|
|
localMedia: LocalMedia?, |
|
|
|
localMedia: LocalMedia?, |
|
|
|
modifier: Modifier = Modifier, |
|
|
|
modifier: Modifier = Modifier, |
|
|
|
info: MediaInfo? = localMedia?.info, |
|
|
|
localMediaViewState: LocalMediaViewState = rememberLocalMediaViewState(), |
|
|
|
onReady: () -> Unit = {}, |
|
|
|
mediaInfo: MediaInfo? = localMedia?.info, |
|
|
|
) { |
|
|
|
) { |
|
|
|
val zoomableState = rememberZoomableState( |
|
|
|
val zoomableState = rememberZoomableState( |
|
|
|
zoomSpec = ZoomSpec(maxZoomFactor = 5f) |
|
|
|
zoomSpec = ZoomSpec(maxZoomFactor = 5f) |
|
|
|
) |
|
|
|
) |
|
|
|
val mimeType = info?.mimeType |
|
|
|
val mimeType = mediaInfo?.mimeType |
|
|
|
when { |
|
|
|
when { |
|
|
|
mimeType.isMimeTypeImage() -> MediaImageView( |
|
|
|
mimeType.isMimeTypeImage() -> MediaImageView( |
|
|
|
|
|
|
|
localMediaViewState = localMediaViewState, |
|
|
|
localMedia = localMedia, |
|
|
|
localMedia = localMedia, |
|
|
|
zoomableState = zoomableState, |
|
|
|
zoomableState = zoomableState, |
|
|
|
onReady = onReady, |
|
|
|
|
|
|
|
modifier = modifier |
|
|
|
modifier = modifier |
|
|
|
) |
|
|
|
) |
|
|
|
mimeType.isMimeTypeVideo() -> MediaVideoView( |
|
|
|
mimeType.isMimeTypeVideo() -> MediaVideoView( |
|
|
|
|
|
|
|
localMediaViewState = localMediaViewState, |
|
|
|
localMedia = localMedia, |
|
|
|
localMedia = localMedia, |
|
|
|
onReady = onReady, |
|
|
|
|
|
|
|
modifier = modifier |
|
|
|
modifier = modifier |
|
|
|
) |
|
|
|
) |
|
|
|
mimeType == MimeTypes.Pdf -> MediaPDFView( |
|
|
|
mimeType == MimeTypes.Pdf -> MediaPDFView( |
|
|
|
|
|
|
|
localMediaViewState = localMediaViewState, |
|
|
|
localMedia = localMedia, |
|
|
|
localMedia = localMedia, |
|
|
|
zoomableState = zoomableState, |
|
|
|
zoomableState = zoomableState, |
|
|
|
onReady = onReady, |
|
|
|
|
|
|
|
modifier = modifier |
|
|
|
modifier = modifier |
|
|
|
) |
|
|
|
) |
|
|
|
else -> MediaFileView( |
|
|
|
else -> MediaFileView( |
|
|
|
|
|
|
|
localMediaViewState = localMediaViewState, |
|
|
|
uri = localMedia?.uri, |
|
|
|
uri = localMedia?.uri, |
|
|
|
info = info, |
|
|
|
info = mediaInfo, |
|
|
|
onReady = onReady, |
|
|
|
|
|
|
|
modifier = modifier |
|
|
|
modifier = modifier |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
@ -111,9 +127,9 @@ fun LocalMediaView( |
|
|
|
|
|
|
|
|
|
|
|
@Composable |
|
|
|
@Composable |
|
|
|
private fun MediaImageView( |
|
|
|
private fun MediaImageView( |
|
|
|
|
|
|
|
localMediaViewState: LocalMediaViewState, |
|
|
|
localMedia: LocalMedia?, |
|
|
|
localMedia: LocalMedia?, |
|
|
|
zoomableState: ZoomableState, |
|
|
|
zoomableState: ZoomableState, |
|
|
|
onReady: () -> Unit, |
|
|
|
|
|
|
|
modifier: Modifier = Modifier, |
|
|
|
modifier: Modifier = Modifier, |
|
|
|
) { |
|
|
|
) { |
|
|
|
if (LocalInspectionMode.current) { |
|
|
|
if (LocalInspectionMode.current) { |
|
|
@ -124,11 +140,7 @@ private fun MediaImageView( |
|
|
|
) |
|
|
|
) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
val zoomableImageState = rememberZoomableImageState(zoomableState) |
|
|
|
val zoomableImageState = rememberZoomableImageState(zoomableState) |
|
|
|
LaunchedEffect(zoomableImageState.isImageDisplayed) { |
|
|
|
localMediaViewState.isReady = zoomableImageState.isImageDisplayed |
|
|
|
if (zoomableImageState.isImageDisplayed) { |
|
|
|
|
|
|
|
onReady() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ZoomableAsyncImage( |
|
|
|
ZoomableAsyncImage( |
|
|
|
modifier = modifier.fillMaxSize(), |
|
|
|
modifier = modifier.fillMaxSize(), |
|
|
|
state = zoomableImageState, |
|
|
|
state = zoomableImageState, |
|
|
@ -142,14 +154,14 @@ private fun MediaImageView( |
|
|
|
@UnstableApi |
|
|
|
@UnstableApi |
|
|
|
@Composable |
|
|
|
@Composable |
|
|
|
fun MediaVideoView( |
|
|
|
fun MediaVideoView( |
|
|
|
|
|
|
|
localMediaViewState: LocalMediaViewState, |
|
|
|
localMedia: LocalMedia?, |
|
|
|
localMedia: LocalMedia?, |
|
|
|
onReady: () -> Unit, |
|
|
|
|
|
|
|
modifier: Modifier = Modifier, |
|
|
|
modifier: Modifier = Modifier, |
|
|
|
) { |
|
|
|
) { |
|
|
|
val context = LocalContext.current |
|
|
|
val context = LocalContext.current |
|
|
|
val playerListener = object : Player.Listener { |
|
|
|
val playerListener = object : Player.Listener { |
|
|
|
override fun onRenderedFirstFrame() { |
|
|
|
override fun onRenderedFirstFrame() { |
|
|
|
onReady() |
|
|
|
localMediaViewState.isReady = true |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
val exoPlayer = remember { |
|
|
|
val exoPlayer = remember { |
|
|
@ -196,35 +208,27 @@ fun MediaVideoView( |
|
|
|
|
|
|
|
|
|
|
|
@Composable |
|
|
|
@Composable |
|
|
|
fun MediaPDFView( |
|
|
|
fun MediaPDFView( |
|
|
|
|
|
|
|
localMediaViewState: LocalMediaViewState, |
|
|
|
localMedia: LocalMedia?, |
|
|
|
localMedia: LocalMedia?, |
|
|
|
zoomableState: ZoomableState, |
|
|
|
zoomableState: ZoomableState, |
|
|
|
onReady: () -> Unit, |
|
|
|
|
|
|
|
modifier: Modifier = Modifier, |
|
|
|
modifier: Modifier = Modifier, |
|
|
|
) { |
|
|
|
) { |
|
|
|
val pdfViewerState = rememberPdfViewerState( |
|
|
|
val pdfViewerState = rememberPdfViewerState( |
|
|
|
model = localMedia?.uri, |
|
|
|
model = localMedia?.uri, |
|
|
|
zoomableState = zoomableState |
|
|
|
zoomableState = zoomableState |
|
|
|
) |
|
|
|
) |
|
|
|
LaunchedEffect(pdfViewerState.isLoaded) { |
|
|
|
localMediaViewState.isReady = pdfViewerState.isLoaded |
|
|
|
if (pdfViewerState.isLoaded) { |
|
|
|
|
|
|
|
onReady() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
PdfViewer(pdfViewerState = pdfViewerState, modifier = modifier) |
|
|
|
PdfViewer(pdfViewerState = pdfViewerState, modifier = modifier) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Composable |
|
|
|
@Composable |
|
|
|
fun MediaFileView( |
|
|
|
fun MediaFileView( |
|
|
|
|
|
|
|
localMediaViewState: LocalMediaViewState, |
|
|
|
uri: Uri?, |
|
|
|
uri: Uri?, |
|
|
|
info: MediaInfo?, |
|
|
|
info: MediaInfo?, |
|
|
|
onReady: () -> Unit, |
|
|
|
|
|
|
|
modifier: Modifier = Modifier, |
|
|
|
modifier: Modifier = Modifier, |
|
|
|
) { |
|
|
|
) { |
|
|
|
LaunchedEffect(Unit) { |
|
|
|
localMediaViewState.isReady = uri != null |
|
|
|
if(uri != null) { |
|
|
|
|
|
|
|
onReady() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Box(modifier = modifier, contentAlignment = Alignment.Center) { |
|
|
|
Box(modifier = modifier, contentAlignment = Alignment.Center) { |
|
|
|
Column(horizontalAlignment = Alignment.CenterHorizontally) { |
|
|
|
Column(horizontalAlignment = Alignment.CenterHorizontally) { |
|
|
|
Box( |
|
|
|
Box( |
|
|
@ -236,14 +240,14 @@ fun MediaFileView( |
|
|
|
) { |
|
|
|
) { |
|
|
|
Icon( |
|
|
|
Icon( |
|
|
|
imageVector = Icons.Outlined.Attachment, |
|
|
|
imageVector = Icons.Outlined.Attachment, |
|
|
|
contentDescription = "OpenFile", |
|
|
|
contentDescription = null, |
|
|
|
tint = MaterialTheme.colorScheme.background, |
|
|
|
tint = MaterialTheme.colorScheme.background, |
|
|
|
modifier = Modifier |
|
|
|
modifier = Modifier |
|
|
|
.size(32.dp) |
|
|
|
.size(32.dp) |
|
|
|
.rotate(-45f), |
|
|
|
.rotate(-45f), |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
if(info == null) return |
|
|
|
if (info != null) { |
|
|
|
Spacer(modifier = Modifier.height(16.dp)) |
|
|
|
Spacer(modifier = Modifier.height(16.dp)) |
|
|
|
Text( |
|
|
|
Text( |
|
|
|
text = info.name, |
|
|
|
text = info.name, |
|
|
@ -255,7 +259,10 @@ fun MediaFileView( |
|
|
|
Text( |
|
|
|
Text( |
|
|
|
text = formatFileExtensionAndSize(info.name, info.formattedFileSize), |
|
|
|
text = formatFileExtensionAndSize(info.name, info.formattedFileSize), |
|
|
|
fontSize = 14.sp, |
|
|
|
fontSize = 14.sp, |
|
|
|
|
|
|
|
maxLines = 1, |
|
|
|
|
|
|
|
overflow = TextOverflow.Ellipsis, |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|