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 package app.myzel394.alibi.ui.components.RecorderScreen.atoms
import android.util.Log
import android.view.ViewGroup import android.view.ViewGroup
import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView import androidx.camera.view.PreviewView
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import app.myzel394.alibi.ui.utils.getCameraProvider
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun CameraPreview( fun CameraPreview(
modifier: Modifier = Modifier, modifier: Modifier,
scaleType: PreviewView.ScaleType = PreviewView.ScaleType.FILL_CENTER,
cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val lifecycleOwner = LocalLifecycleOwner.current 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 Box(modifier = modifier) {
val previewUseCase = Preview.Builder() // Video preview
.build() AndroidView(
.also { factory = { context ->
it.setSurfaceProvider(previewView.surfaceProvider) val previewView = PreviewView(context).apply {
} layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
coroutineScope.launch { ViewGroup.LayoutParams.MATCH_PARENT,
val cameraProvider = ProcessCameraProvider.getInstance(context).get()
try {
// Must unbind the use-cases before rebinding them.
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, previewUseCase
) )
} 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.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.alibi.R import app.myzel394.alibi.R
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraPreview
import app.myzel394.alibi.dataStore import app.myzel394.alibi.dataStore
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveCurrentNowModal import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveCurrentNowModal
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus
@ -54,68 +55,79 @@ fun VideoRecordingStatus(
when (orientation) { when (orientation) {
Configuration.ORIENTATION_LANDSCAPE -> { Configuration.ORIENTATION_LANDSCAPE -> {
Row( Box {
modifier = Modifier.fillMaxSize(), CameraPreview(
horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier,
verticalAlignment = Alignment.CenterVertically, cameraSelector = videoRecorder.cameraSelector
) { )
Column( Row(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement horizontalArrangement = Arrangement.SpaceEvenly,
.spacedBy(32.dp), verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.weight(1f)
.fillMaxWidth(0.9f)
.align(Alignment.CenterVertically),
) {
_VideoGeneralInfo(videoRecorder)
_VideoRecordingStatus(videoRecorder)
}
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth(0.9f)
) { ) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement verticalArrangement = Arrangement
.spacedBy(32.dp), .spacedBy(32.dp),
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
.weight(1f)
.fillMaxWidth(0.9f)
.align(Alignment.CenterVertically),
) { ) {
_VideoControls(videoRecorder) _VideoGeneralInfo(videoRecorder)
HorizontalDivider() _VideoRecordingStatus(videoRecorder)
_PrimitiveControls(videoRecorder) }
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth(0.9f)
) {
Column(
verticalArrangement = Arrangement
.spacedBy(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
_VideoControls(videoRecorder)
HorizontalDivider()
_PrimitiveControls(videoRecorder)
}
} }
} }
} }
} }
else -> { else -> {
Column( Box {
modifier = Modifier CameraPreview(
.fillMaxSize() modifier = Modifier,
.padding(bottom = 32.dp), cameraSelector = videoRecorder.cameraSelector
horizontalAlignment = Alignment.CenterHorizontally, )
verticalArrangement = Arrangement.SpaceBetween,
) {
Box {}
Column( Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement verticalArrangement = Arrangement.SpaceBetween,
.spacedBy(16.dp),
) { ) {
_VideoGeneralInfo(videoRecorder) Column(
_VideoRecordingStatus(videoRecorder) horizontalAlignment = Alignment.CenterHorizontally,
} verticalArrangement = Arrangement
.spacedBy(16.dp),
) {
_VideoGeneralInfo(videoRecorder)
_VideoRecordingStatus(videoRecorder)
}
Column( Column(
verticalArrangement = Arrangement verticalArrangement = Arrangement
.spacedBy(16.dp), .spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
_VideoControls(videoRecorder) _VideoControls(videoRecorder)
HorizontalDivider() HorizontalDivider()
_PrimitiveControls(videoRecorder) _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))
}
}