После отладки я узнал, что проблема заключается в
ontap функции модификатора, где он дает неправильный смещение по какой -то причине?
. вещи?
Код: Выделить всё
@Composable
fun CameraScreen(
modifier: Modifier = Modifier,
permissionStatus: Boolean?,
state: CameraState,
onEvent: (CameraEvent) -> Unit = {},
viewModel: CameraViewModel = hiltViewModel(),
onNavigateToImageEdit : (AppScreen.MediaEdit) -> Unit
) {
val context = LocalContext.current
val app = context.applicationContext
val lifecycleOwner = LocalLifecycleOwner.current
var showImagePreview by remember { mutableStateOf(false) }
val imageUri by viewModel.capturedImageUri.collectAsStateWithLifecycle()
var co by remember { mutableStateOf(Offset(0f,0f)) }
// val ratio = if(state.aspectRatio == AspectRatio.RATIO_16_9)
val mediaLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia()
) { uri ->
if (uri!=null){
Log.d("CameraScreen", "Camera Screen content uri : ${uri.toString()} ")
onNavigateToImageEdit(AppScreen.MediaEdit(uri.toString()))
}
}
LaunchedEffect(Unit) {
viewModel.errorFlow.collect { message ->
Log.e(TAG, "CameraScreen: error while capturing $message")
}
}
val coordinateTransformer = remember { MutableCoordinateTransformer() }
var autofocusRequest by remember { mutableStateOf(UUID.randomUUID() to Offset.Unspecified) }
val autofocusRequestId = autofocusRequest.first
// Show the autofocus indicator if the offset is specified
var showAutofocusIndicator = autofocusRequest.second.isSpecified
// Cache the initial coords for each autofocus request
val autofocusCoords = remember(autofocusRequestId) { autofocusRequest.second }
// Queue hiding the request for each unique autofocus tap
if (showAutofocusIndicator) {
LaunchedEffect(autofocusRequestId) {
delay(2000)
autofocusRequest = autofocusRequestId to Offset.Unspecified
// if (!isUserInteractingWithSlider) {
//
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
,
) {
Log.d(TAG, "CameraScreen: permissionStatus = ${permissionStatus} ")
if (permissionStatus != null && !permissionStatus) {
Text(
text = "Camera permission has not been granted",
modifier = Modifier.align(Alignment.Center)
)
}
if (permissionStatus != null && permissionStatus) {
Text(
text = "Camera",
modifier = Modifier.align(Alignment.Center)
)
}
state.surfaceRequest?.let { surfaceRequest ->
CameraXViewfinder(
surfaceRequest = surfaceRequest,
coordinateTransformer = coordinateTransformer,
modifier = Modifier
.align(Alignment.Center)
.fillMaxWidth()
.aspectRatio(state.aspectRatio.ratio)
.pointerInput(Unit) {
detectTapGestures(
onDoubleTap = { tapCoords ->
onEvent(CameraEvent.ChangeLensFacing)
},
onTap = {offset ->
co = offset
with(coordinateTransformer){
onEvent(CameraEvent.TapToFocus(offset.transform()))
}
autofocusRequest = UUID.randomUUID() to offset
}
)
}
.pointerInput(Unit) {
// detectTransformGestures { _, _, zoom, _ ->
// val scale = (state.zoomScale + (zoom - 1f)).coerceIn(0f, 1f)
// Log.d(TAG, "zoom scale : $scale")
// onEvent(CameraEvent.Zoom(scale))
// }
}
)
AnimatedVisibility(
visible = showAutofocusIndicator,
enter = fadeIn(),
exit = fadeOut(),
modifier = Modifier
) {
Spacer(
Modifier
.offset { autofocusCoords.takeOrElse { Offset.Zero }.round() }
.offset((-24).dp, (-24).dp)
.border(1.dp, Color.White, CircleShape)
.size(48.dp)
)
}
}
UpperBox(
modifier = Modifier.align(Alignment.TopEnd),
torchState = state.torchState,
onTorchToggle = {
onEvent(CameraEvent.TorchToggle)
},
onAspectRatioChange = {
onEvent(CameraEvent.ToggleAspectRatio)
}
)
LowerBox(
modifier = Modifier
.align(Alignment.BottomCenter),
onToggleCamera = {
onEvent(CameraEvent.ChangeLensFacing)
},
onChooseFromGallery = {
mediaLauncher.launch(
PickVisualMediaRequest(
ActivityResultContracts.PickVisualMedia.ImageOnly
)
)
},
onClick = {
val file = createTempFile(
context
)
onEvent(CameraEvent.TakePicture(file))
}
)
// tap indicator for debugging
Surface(
modifier = Modifier
.offset{co.round()}
.height(10.dp).width(10.dp)
.background(Color.White)
) {
}
}
LaunchedEffect(imageUri) {
if(imageUri!=null){
onNavigateToImageEdit(AppScreen.MediaEdit(imageUri.toString()))
onEvent(CameraEvent.Reset)
}
}
LaunchedEffect(lifecycleOwner, state.lensFacing,state.aspectRatio) {
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
onEvent(CameraEvent.Preview(app, lifecycleOwner))
}
}
}
Код: Выделить всё
state.surfaceRequest?.let { surfaceRequest ->
CameraXViewfinder(
surfaceRequest = surfaceRequest,
coordinateTransformer = coordinateTransformer,
modifier = Modifier
.align(Alignment.Center)
.fillMaxWidth()
.aspectRatio(state.aspectRatio.ratio)
.pointerInput(Unit) {
detectTapGestures(
onDoubleTap = { tapCoords ->
onEvent(CameraEvent.ChangeLensFacing)
},
onTap = {offset ->
co = offset
with(coordinateTransformer){
onEvent(CameraEvent.TapToFocus(offset.transform()))
}
autofocusRequest = UUID.randomUUID() to offset
}
)
}
.pointerInput(Unit) {
// detectTransformGestures { _, _, zoom, _ ->
// val scale = (state.zoomScale + (zoom - 1f)).coerceIn(0f, 1f)
// Log.d(TAG, "zoom scale : $scale")
// onEvent(CameraEvent.Zoom(scale))
// }
}
)
Код: Выделить всё
package com.example.memories.feature.feature_camera.data.data_source
import android.content.Context
import android.util.Log
import androidx.camera.core.CameraControl
import androidx.camera.core.CameraInfo
import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraSelector.LENS_FACING_BACK
import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
import androidx.camera.core.FocusMeteringAction
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.MeteringPoint
import androidx.camera.core.Preview
import androidx.camera.core.SurfaceOrientedMeteringPointFactory
import androidx.camera.core.SurfaceRequest
import androidx.camera.core.UseCaseGroup
import androidx.camera.core.resolutionselector.AspectRatioStrategy
import androidx.camera.core.resolutionselector.ResolutionSelector
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.lifecycle.awaitInstance
import androidx.compose.ui.geometry.Offset
import androidx.lifecycle.LifecycleOwner
import com.example.memories.feature.feature_camera.domain.model.AspectRatio
import com.example.memories.feature.feature_camera.domain.model.CaptureResult
import com.example.memories.feature.feature_camera.domain.model.LensFacing
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.suspendCancellableCoroutine
import java.io.File
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import kotlin.coroutines.resume
class CameraManager {
companion object {
private const val TAG = "CameraManager"
}
private var surfaceRequestCallback: ((SurfaceRequest) -> Unit)? = null
private var cameraControl: CameraControl? = null
private var cameraInfo: CameraInfo? = null
private lateinit var cameraPreviewUseCase: Preview
private lateinit var imageCaptureUseCase: ImageCapture
private lateinit var processCameraProvider: ProcessCameraProvider
private lateinit var surfaceMeteringPointFactory: SurfaceOrientedMeteringPointFactory
private val resolutionSelectorBuilder = ResolutionSelector.Builder()
// private val cameraPreviewUseCase = Preview.Builder().build().apply {
// setSurfaceProvider { surfaceRequest ->
// surfaceRequestCallback?.invoke(surfaceRequest)
// }
//
// }
//
// private val imageCaptureUseCase = ImageCapture.Builder()
// .setTargetRotation(cameraPreviewUseCase!!.targetRotation)
// .build()
init {
setAspectRatio(AspectRatio.RATIO_4_3)
// initUseCases()
}
fun initUseCases() {
cameraPreviewUseCase = Preview.Builder()
.setResolutionSelector(resolutionSelectorBuilder.build())
.build()
cameraPreviewUseCase!!.setSurfaceProvider { surfaceRequest ->
surfaceRequestCallback?.invoke(surfaceRequest)
surfaceMeteringPointFactory = SurfaceOrientedMeteringPointFactory(
surfaceRequest.resolution.width.toFloat(),
surfaceRequest.resolution.height.toFloat())
}
imageCaptureUseCase = ImageCapture.Builder()
.setTargetRotation(cameraPreviewUseCase!!.targetRotation)
.setResolutionSelector(resolutionSelectorBuilder.build())
.build()
}
suspend fun bindToCamera(
appContext: Context,
lifecycleOwner: LifecycleOwner,
lensFacing: LensFacing = LensFacing.BACK,
torch: Boolean = false
) {
processCameraProvider = ProcessCameraProvider.awaitInstance(appContext)
unbind(processCameraProvider)
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(if (lensFacing == LensFacing.BACK) LENS_FACING_BACK else LENS_FACING_FRONT)
.build()
val camera = processCameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
UseCaseGroup.Builder()
.addUseCase(cameraPreviewUseCase)
.addUseCase(imageCaptureUseCase)
.build()
)
cameraControl = camera.cameraControl
cameraInfo = camera.cameraInfo
cameraControl?.enableTorch(torch)
Log.d(TAG, "Torch Value : ${torch}")
// Cancellation signals we're done with the camera
try {
awaitCancellation()
} finally {
unbind(processCameraProvider)
}
}
fun unbind(processCameraProvider: ProcessCameraProvider) {
processCameraProvider.unbindAll()
}
fun setSurfaceRequestCallback(callback: (SurfaceRequest) -> Unit) {
surfaceRequestCallback = callback
}
fun tapToFocus(tapCoords: Offset) {
Log.d(TAG, "tapToFocus: offset = ${tapCoords}")
val point: MeteringPoint? =
surfaceMeteringPointFactory?.createPoint(tapCoords.x, tapCoords.y)
if (point != null) {
val meteringAction = FocusMeteringAction.Builder(point).build()
cameraControl?.startFocusAndMetering(meteringAction)
}
Log.d(TAG, "tapToFocus: called")
}
fun setAspectRatio(aspectRatio: AspectRatio = AspectRatio.RATIO_4_3) {
val aspect =
if (aspectRatio == AspectRatio.RATIO_4_3) AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY
else AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY
setAspect(aspect)
initUseCases()
Log.d(
"CameraManager",
"Aspect Ratio : ${resolutionSelectorBuilder.build().aspectRatioStrategy}"
)
}
private fun setAspect(aspect: AspectRatioStrategy) {
resolutionSelectorBuilder.setAspectRatioStrategy(aspect)
}
@Throws(NullPointerException::class)
fun torchToggle(torch: Boolean) {
if (cameraControl == null) throw NullPointerException("Camera Control Null")
cameraControl?.enableTorch(torch)
}
fun zoom(scale: Float) {
cameraControl?.setLinearZoom(scale)
}
suspend fun takePicture(
file: File
): CaptureResult {
if (imageCaptureUseCase == null) {
val error = IllegalStateException("ImageCapture use case not initialized")
Log.e(TAG, "${error.message}")
return CaptureResult.Error(error)
}
return suspendCancellableCoroutine { continuation ->
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
val imageSavedCallback = object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
Log.d(TAG, "${outputFileResults.savedUri}")
if (outputFileResults.savedUri == null) {
Log.e(TAG, "onImageSaved: savedUri is null")
}
continuation.resume(CaptureResult.Success(outputFileResults.savedUri))
}
override fun onError(exception: ImageCaptureException) {
Log.e(TAG, "${exception.message}")
continuation.resume(CaptureResult.Error(exception))
}
}
continuation.invokeOnCancellation {
Log.d(TAG, "Coroutine Cancelled")
}
val executor: Executor = Executors.newSingleThreadExecutor()
imageCaptureUseCase.takePicture(outputFileOptions, executor, imageSavedCallback)
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/796 ... t-position
Мобильная версия