feature: adds camera preview

This commit is contained in:
Roy Matero 2024-02-24 14:24:49 -08:00
parent 38fc299fc6
commit 6f8b68c7b2
2 changed files with 76 additions and 1 deletions

View File

@ -1,17 +1,23 @@
package app.myzel394.alibi.ui.components.RecorderScreen.organisms package app.myzel394.alibi.ui.components.RecorderScreen.organisms
import android.content.res.Configuration 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.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
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.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
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.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.filled.CameraAlt
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
@ -26,11 +32,14 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue 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.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import app.myzel394.alibi.R import app.myzel394.alibi.R
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus
import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingControl 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.models.VideoRecorderModel
import app.myzel394.alibi.ui.utils.CameraInfo import app.myzel394.alibi.ui.utils.CameraInfo
import app.myzel394.alibi.ui.utils.KeepScreenOn import app.myzel394.alibi.ui.utils.KeepScreenOn
import app.myzel394.alibi.ui.utils.getCameraProvider
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -79,6 +89,12 @@ fun VideoRecordingStatus(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
_VideoControls(videoRecorder) _VideoControls(videoRecorder)
CameraPreview(
videoRecorder,
modifier = Modifier
.aspectRatio(5 / 2F)
.padding(horizontal = 12.dp)
)
HorizontalDivider() HorizontalDivider()
_PrimitiveControls(videoRecorder) _PrimitiveControls(videoRecorder)
} }
@ -94,7 +110,11 @@ fun VideoRecordingStatus(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween, verticalArrangement = Arrangement.SpaceBetween,
) { ) {
Box {} CameraPreview(
videoRecorder, modifier = Modifier
.padding(24.dp)
.aspectRatio(3 / 2F)
)
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, 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 @Composable
fun _VideoGeneralInfo(videoRecorder: VideoRecorderModel) { fun _VideoGeneralInfo(videoRecorder: VideoRecorderModel) {
val context = LocalContext.current val context = LocalContext.current

View File

@ -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))
}
}