Merge pull request #81 from materoy/master

Add camera preview
This commit is contained in:
Myzel394 2024-03-23 12:29:33 +01:00 committed by GitHub
commit 24928661c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 107 additions and 79 deletions

View File

@ -1,56 +1,56 @@
package app.myzel394.alibi.ui.components.RecorderScreen.atoms
import android.util.Log
import android.view.ViewGroup
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.viewinterop.AndroidView
import app.myzel394.alibi.ui.utils.getCameraProvider
import kotlinx.coroutines.launch
@Composable
fun CameraPreview(
modifier: Modifier = Modifier,
scaleType: PreviewView.ScaleType = PreviewView.ScaleType.FILL_CENTER,
modifier: Modifier,
cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
) {
val coroutineScope = rememberCoroutineScope()
val lifecycleOwner = LocalLifecycleOwner.current
AndroidView(
modifier = modifier,
factory = { context ->
val previewView = PreviewView(context).apply {
this.scaleType = scaleType
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
// CameraX Preview UseCase
val previewUseCase = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
coroutineScope.launch {
val cameraProvider = ProcessCameraProvider.getInstance(context).get()
try {
// Must unbind the use-cases before rebinding them.
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, previewUseCase
Box(modifier = modifier) {
// Video preview
AndroidView(
factory = { context ->
val previewView = PreviewView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
)
} catch (ex: Exception) {
}
}
val previewUseCase = Preview.Builder()
.build()
.also { it.setSurfaceProvider(previewView.surfaceProvider) }
previewView
}
)
coroutineScope.launch {
val cameraProvider = context.getCameraProvider()
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
previewUseCase
)
} catch (ex: Exception) {
Log.e("CameraPreview", "Use case binding failed", ex)
}
}
previewView
},
)
}
}

View File

@ -33,6 +33,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import app.myzel394.alibi.R
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraPreview
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveCurrentNowModal
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus
@ -54,68 +55,79 @@ fun VideoRecordingStatus(
when (orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement
.spacedBy(32.dp),
modifier = Modifier
.weight(1f)
.fillMaxWidth(0.9f)
.align(Alignment.CenterVertically),
) {
_VideoGeneralInfo(videoRecorder)
_VideoRecordingStatus(videoRecorder)
}
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth(0.9f)
Box {
CameraPreview(
modifier = Modifier,
cameraSelector = videoRecorder.cameraSelector
)
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement
.spacedBy(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.weight(1f)
.fillMaxWidth(0.9f)
.align(Alignment.CenterVertically),
) {
_VideoControls(videoRecorder)
HorizontalDivider()
_PrimitiveControls(videoRecorder)
_VideoGeneralInfo(videoRecorder)
_VideoRecordingStatus(videoRecorder)
}
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth(0.9f)
) {
Column(
verticalArrangement = Arrangement
.spacedBy(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
_VideoControls(videoRecorder)
HorizontalDivider()
_PrimitiveControls(videoRecorder)
}
}
}
}
}
else -> {
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween,
) {
Box {}
Box {
CameraPreview(
modifier = Modifier,
cameraSelector = videoRecorder.cameraSelector
)
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement
.spacedBy(16.dp),
verticalArrangement = Arrangement.SpaceBetween,
) {
_VideoGeneralInfo(videoRecorder)
_VideoRecordingStatus(videoRecorder)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement
.spacedBy(16.dp),
) {
_VideoGeneralInfo(videoRecorder)
_VideoRecordingStatus(videoRecorder)
}
Column(
verticalArrangement = Arrangement
.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
_VideoControls(videoRecorder)
HorizontalDivider()
_PrimitiveControls(videoRecorder)
Column(
verticalArrangement = Arrangement
.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
_VideoControls(videoRecorder)
HorizontalDivider()
_PrimitiveControls(videoRecorder)
}
}
}
}

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