camerax with preview
This commit is contained in:
@@ -49,6 +49,11 @@ dependencies {
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.camera.core)
|
||||
implementation(libs.androidx.camera.camera2)
|
||||
implementation(libs.androidx.camera.lifecycle)
|
||||
implementation(libs.androidx.camera.view)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
|
||||
@@ -4,14 +4,19 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.mydomain.myscan.ui.theme.MyScanTheme
|
||||
import org.mydomain.myscan.view.CameraScreen
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -19,11 +24,13 @@ class MainActivity : ComponentActivity() {
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
MyScanTheme {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
Greeting(
|
||||
name = "Android",
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
)
|
||||
Scaffold(/*modifier = Modifier.fillMaxSize()*/) { innerPadding ->
|
||||
Column {
|
||||
Greeting(modifier = Modifier.padding(innerPadding))
|
||||
Box(/*modifier = Modifier.width(300.dp)*/) {
|
||||
CameraScreen { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,9 +38,9 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Greeting(name: String, modifier: Modifier = Modifier) {
|
||||
fun Greeting(modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = "Hello $name!",
|
||||
text = "Scan your document",
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
@@ -42,6 +49,6 @@ fun Greeting(name: String, modifier: Modifier = Modifier) {
|
||||
@Composable
|
||||
fun GreetingPreview() {
|
||||
MyScanTheme {
|
||||
Greeting("Android")
|
||||
Greeting()
|
||||
}
|
||||
}
|
||||
114
app/src/main/java/org/mydomain/myscan/view/Camera.kt
Normal file
114
app/src/main/java/org/mydomain/myscan/view/Camera.kt
Normal file
@@ -0,0 +1,114 @@
|
||||
package org.mydomain.myscan.view
|
||||
|
||||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.camera.core.AspectRatio.RATIO_4_3
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.camera.core.ImageProxy
|
||||
import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
@Composable
|
||||
fun CameraScreen(
|
||||
onImageAnalyzed: (ImageProxy) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val requestPermissionLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
if (!isGranted) {
|
||||
Toast.makeText(context, "Camera permission was denied", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
val camera = android.Manifest.permission.CAMERA
|
||||
if (ContextCompat.checkSelfPermission(context, camera) != PERMISSION_GRANTED) {
|
||||
requestPermissionLauncher.launch(camera)
|
||||
}
|
||||
}
|
||||
|
||||
CameraPreview(onImageAnalyzed = { imageProxy -> onImageAnalyzed(imageProxy) })
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CameraPreview(
|
||||
modifier: Modifier = Modifier,
|
||||
onImageAnalyzed: (ImageProxy) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val cameraProviderFuture by remember {
|
||||
mutableStateOf(ProcessCameraProvider.getInstance(context))
|
||||
}
|
||||
|
||||
DisposableEffect(lifecycleOwner) {
|
||||
onDispose {
|
||||
cameraProviderFuture.get().unbindAll()
|
||||
}
|
||||
}
|
||||
|
||||
AndroidView(modifier = modifier, factory = {
|
||||
val previewView = PreviewView(it).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||
scaleType = PreviewView.ScaleType.FIT_CENTER
|
||||
}
|
||||
|
||||
val executor = Executors.newSingleThreadExecutor()
|
||||
cameraProviderFuture.addListener({
|
||||
bindCameraUseCases(
|
||||
lifecycleOwner = lifecycleOwner,
|
||||
cameraProviderFuture = cameraProviderFuture,
|
||||
executor = executor,
|
||||
previewView = previewView,
|
||||
onImageAnalyzed = onImageAnalyzed
|
||||
)
|
||||
}, ContextCompat.getMainExecutor(context))
|
||||
|
||||
previewView
|
||||
})
|
||||
}
|
||||
|
||||
fun bindCameraUseCases(
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
cameraProviderFuture: ListenableFuture<ProcessCameraProvider>,
|
||||
executor: ExecutorService,
|
||||
previewView: PreviewView,
|
||||
onImageAnalyzed: (ImageProxy) -> Unit,
|
||||
) {
|
||||
val preview: Preview = Preview.Builder().setTargetAspectRatio(RATIO_4_3).build()
|
||||
|
||||
preview.surfaceProvider = previewView.surfaceProvider
|
||||
|
||||
val cameraSelector: CameraSelector =
|
||||
CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
|
||||
val imageAnalysis = ImageAnalysis.Builder().setTargetAspectRatio(RATIO_4_3)
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888).build()
|
||||
imageAnalysis.setAnalyzer(executor, onImageAnalyzed)
|
||||
|
||||
val cameraProvider = cameraProviderFuture.get()
|
||||
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis, preview)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user