I want to use only square crop of the image from sensor, therefore I want to have square preview in UI, other UI elements are supposed to be placed outside of preview. UI is built using jetpack compose. But somehow I'm getting preview looking like 3:4 rectangle, and preview is overlapping with other UI elements, which are arranged as expected for square preview.
Project illustrating the problem on github: https://github.com/skitov/android_camera
Code that I have tried
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyApplicationTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Content(
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
@Composable
fun CameraPreview()
{
val previewH = (LocalConfiguration.current.screenWidthDp).dp
fun startCamera(context: Context,
previewView: PreviewView) {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// CameraSelector for back camera
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
// Set up Preview use case
val preview = Preview.Builder().build().also {
it.surfaceProvider = previewView.surfaceProvider
}
try {
// Unbind all use cases before rebinding
cameraProvider.unbindAll()
val viewPort = ViewPort.Builder(Rational(1,1), Surface.ROTATION_0).
setScaleType(ViewPort.FILL_CENTER).build()
val useGrp = UseCaseGroup.Builder().
addUseCase(preview).
addUseCase(ImageAnalysis.Builder().build()).
setViewPort(viewPort).build()
// Bind the camera to the lifecycle and the Preview use case
cameraProvider.bindToLifecycle(context as LifecycleOwner,
cameraSelector, useGrp)
} catch (exc: Exception) {
Log.e("CameraXApp", "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(context))
}
Box(modifier = Modifier.fillMaxWidth().height(previewH))
{
AndroidView(
modifier = Modifier.fillMaxSize()
.onGloballyPositioned { c ->
Log.i("PREVIEW", "Androview size: ${c.size}")
}
,
factory = { context ->
val screenWidth = Resources.getSystem().displayMetrics.widthPixels
val previewView = PreviewView(context).apply {
scaleType = PreviewView.ScaleType.FILL_CENTER
layoutParams = ViewGroup.LayoutParams(
screenWidth,
screenWidth
)
}
//focusFactory = previewView.meteringPointFactory
startCamera(context, previewView)
previewView
}
)
}
}
@Composable
fun Content(modifier: Modifier = Modifier)
{
val ctx = LocalContext.current
val needPermission = remember {
mutableStateOf(ContextCompat.checkSelfPermission(ctx, Manifest.permission.CAMERA) !=
PackageManager.PERMISSION_GRANTED)}
val CAMERA_PERMISSION_REQUEST_CODE = 101
@Composable
fun PermissionRequestUI() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Camera permission is required to use this feature.")
Button(onClick = { ActivityCompat.requestPermissions(
ctx as Activity,
arrayOf(Manifest.permission.CAMERA),
CAMERA_PERMISSION_REQUEST_CODE
)
needPermission.value = false}) {
Text("Grant Permission")
}
}
}
if(needPermission.value)
{
PermissionRequestUI()
}
else {
Column(modifier = modifier)
{
CameraPreview()
Text("ABYR")
Text("VALG")
Text("qwert")
Text("zxcv")
}
}
}