|
|
@@ -1,7 +1,8 @@
|
|
|
package com.joe.camera.webcam.ui
|
|
|
|
|
|
-import android.content.Context
|
|
|
+import android.media.MediaMetadataRetriever
|
|
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
|
+import androidx.compose.foundation.Image
|
|
|
import androidx.compose.foundation.background
|
|
|
import androidx.compose.foundation.combinedClickable
|
|
|
import androidx.compose.foundation.layout.*
|
|
|
@@ -15,6 +16,8 @@ import androidx.compose.runtime.*
|
|
|
import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
|
import androidx.compose.ui.graphics.Color
|
|
|
+import androidx.compose.ui.graphics.ImageBitmap
|
|
|
+import androidx.compose.ui.graphics.asImageBitmap
|
|
|
import androidx.compose.ui.graphics.graphicsLayer
|
|
|
import androidx.compose.ui.layout.ContentScale
|
|
|
import androidx.compose.ui.platform.LocalContext
|
|
|
@@ -24,15 +27,13 @@ import androidx.media3.common.MediaItem as ExoMediaItem
|
|
|
import androidx.media3.common.MimeTypes
|
|
|
import androidx.media3.exoplayer.ExoPlayer
|
|
|
import androidx.media3.ui.PlayerView
|
|
|
-import coil.ImageLoader
|
|
|
import coil.compose.AsyncImage
|
|
|
-import coil.decode.VideoFrameDecoder
|
|
|
-import coil.request.ImageRequest
|
|
|
-import coil.request.videoFrameMillis
|
|
|
import com.joe.camera.webcam.MainViewModel
|
|
|
import com.joe.camera.webcam.model.MediaItem
|
|
|
import com.joe.camera.webcam.model.MediaKind
|
|
|
import kotlinx.coroutines.delay
|
|
|
+import kotlinx.coroutines.Dispatchers
|
|
|
+import kotlinx.coroutines.withContext
|
|
|
|
|
|
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
|
|
@Composable
|
|
|
@@ -58,9 +59,36 @@ fun MediaGridScreen(viewModel: MainViewModel, media: List<MediaItem>, onBack: ()
|
|
|
@Composable
|
|
|
private fun MediaThumbnail(item: MediaItem, modifier: Modifier = Modifier) {
|
|
|
val context = LocalContext.current
|
|
|
- val imageLoader = remember(context) { ImageLoader.Builder(context).components { add(VideoFrameDecoder.Factory()) }.build() }
|
|
|
- val request = remember(item.uri) { ImageRequest.Builder(context).data(item.uri).apply { if (item.kind == MediaKind.VIDEO) videoFrameMillis(0) }.crossfade(true).build() }
|
|
|
- AsyncImage(model = request, imageLoader = imageLoader, contentDescription = item.displayName, contentScale = ContentScale.Crop, modifier = modifier)
|
|
|
+ if (item.kind == MediaKind.IMAGE) {
|
|
|
+ AsyncImage(
|
|
|
+ model = item.uri,
|
|
|
+ contentDescription = item.displayName,
|
|
|
+ contentScale = ContentScale.Crop,
|
|
|
+ modifier = modifier,
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ val thumbnail by produceState<ImageBitmap?>(initialValue = null, item.uri) {
|
|
|
+ value = withContext(Dispatchers.IO) {
|
|
|
+ val retriever = MediaMetadataRetriever()
|
|
|
+ try {
|
|
|
+ retriever.setDataSource(context, item.uri)
|
|
|
+ retriever.getFrameAtTime(0L, MediaMetadataRetriever.OPTION_CLOSEST_SYNC)?.asImageBitmap()
|
|
|
+ } catch (_: Exception) {
|
|
|
+ null
|
|
|
+ } finally {
|
|
|
+ retriever.release()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ thumbnail?.let {
|
|
|
+ Image(
|
|
|
+ bitmap = it,
|
|
|
+ contentDescription = item.displayName,
|
|
|
+ contentScale = ContentScale.Crop,
|
|
|
+ modifier = modifier,
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|