diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 47479d9..a269bb6 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -1,17 +1,23 @@ package app.myzel394.alibi.ui.components.RecorderScreen.organisms import android.content.res.Configuration +import android.util.Log +import android.view.ViewGroup +import androidx.camera.core.Preview +import androidx.camera.view.PreviewView import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material3.HorizontalDivider @@ -26,11 +32,14 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView import app.myzel394.alibi.R import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingControl @@ -38,6 +47,7 @@ import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingStatus import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.utils.CameraInfo import app.myzel394.alibi.ui.utils.KeepScreenOn +import app.myzel394.alibi.ui.utils.getCameraProvider import com.valentinilk.shimmer.shimmer import kotlinx.coroutines.launch @@ -79,6 +89,12 @@ fun VideoRecordingStatus( horizontalAlignment = Alignment.CenterHorizontally, ) { _VideoControls(videoRecorder) + CameraPreview( + videoRecorder, + modifier = Modifier + .aspectRatio(5 / 2F) + .padding(horizontal = 12.dp) + ) HorizontalDivider() _PrimitiveControls(videoRecorder) } @@ -94,7 +110,11 @@ fun VideoRecordingStatus( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceBetween, ) { - Box {} + CameraPreview( + videoRecorder, modifier = Modifier + .padding(24.dp) + .aspectRatio(3 / 2F) + ) Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -120,6 +140,45 @@ fun VideoRecordingStatus( } +@Composable +fun CameraPreview(videoRecorder: VideoRecorderModel, modifier: Modifier) { + val coroutineScope = rememberCoroutineScope() + val lifecycleOwner = LocalLifecycleOwner.current + + Box(modifier = modifier.clip(RoundedCornerShape(12.dp))) { + + // Video preview + AndroidView( + factory = { context -> + val previewView = PreviewView(context).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ) + } + val previewUseCase = Preview.Builder() + .build() + .also { it.setSurfaceProvider(previewView.surfaceProvider) } + + coroutineScope.launch { + val cameraProvider = context.getCameraProvider() + try { + cameraProvider.unbindAll() + cameraProvider.bindToLifecycle( + lifecycleOwner, + videoRecorder.cameraSelector, + previewUseCase + ) + } catch (ex: Exception) { + Log.e("CameraPreview", "Use case binding failed", ex) + } + } + previewView + }, + ) + } +} + @Composable fun _VideoGeneralInfo(videoRecorder: VideoRecorderModel) { val context = LocalContext.current diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/Context.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/Context.kt new file mode 100644 index 0000000..058f7dd --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/Context.kt @@ -0,0 +1,16 @@ +package app.myzel394.alibi.ui.utils + +import android.content.Context +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.core.content.ContextCompat +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +suspend fun Context.getCameraProvider(): ProcessCameraProvider = suspendCoroutine { continuation -> + ProcessCameraProvider.getInstance(this).also { future -> + future.addListener({ + continuation.resume(future.get()) + }, ContextCompat.getMainExecutor(this)) + } +} +