mirror of
https://github.com/SimpleMobileTools/Simple-Camera.git
synced 2025-06-27 09:02:59 +02:00
update focus and unbind useCase
- update to use the DisplayOrientedMeteringPointFactory - add Auto Focus (AF) and Auto Exposure (AE) metering points - unbind VideoCaptureUseCase when in photo mode - unbind ImageCaptureUseCase when not in photo mode
This commit is contained in:
@ -2,11 +2,14 @@ package com.simplemobiletools.camera.implementations
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
|
import android.hardware.display.DisplayManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.Display
|
||||||
import android.view.GestureDetector
|
import android.view.GestureDetector
|
||||||
import android.view.GestureDetector.SimpleOnGestureListener
|
import android.view.GestureDetector.SimpleOnGestureListener
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
@ -44,7 +47,6 @@ import com.simplemobiletools.camera.helpers.MediaSoundHelper
|
|||||||
import com.simplemobiletools.camera.interfaces.MyPreview
|
import com.simplemobiletools.camera.interfaces.MyPreview
|
||||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||||
import com.simplemobiletools.commons.extensions.toast
|
import com.simplemobiletools.commons.extensions.toast
|
||||||
import java.lang.IllegalArgumentException
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@ -54,7 +56,7 @@ import kotlin.math.min
|
|||||||
|
|
||||||
class CameraXPreview(
|
class CameraXPreview(
|
||||||
private val activity: AppCompatActivity,
|
private val activity: AppCompatActivity,
|
||||||
private val viewFinder: PreviewView,
|
private val previewView: PreviewView,
|
||||||
private val listener: CameraXPreviewListener,
|
private val listener: CameraXPreviewListener,
|
||||||
) : MyPreview, DefaultLifecycleObserver {
|
) : MyPreview, DefaultLifecycleObserver {
|
||||||
|
|
||||||
@ -62,13 +64,19 @@ class CameraXPreview(
|
|||||||
private const val TAG = "CameraXPreview"
|
private const val TAG = "CameraXPreview"
|
||||||
private const val RATIO_4_3_VALUE = 4.0 / 3.0
|
private const val RATIO_4_3_VALUE = 4.0 / 3.0
|
||||||
private const val RATIO_16_9_VALUE = 16.0 / 9.0
|
private const val RATIO_16_9_VALUE = 16.0 / 9.0
|
||||||
|
|
||||||
|
// Auto focus is 1/6 of the area.
|
||||||
|
private const val AF_SIZE = 1.0f / 6.0f
|
||||||
|
private const val AE_SIZE = AF_SIZE * 1.5f
|
||||||
}
|
}
|
||||||
|
|
||||||
private val config = activity.config
|
private val config = activity.config
|
||||||
private val contentResolver = activity.contentResolver
|
private val contentResolver = activity.contentResolver
|
||||||
private val mainExecutor = activity.mainExecutor
|
private val mainExecutor = activity.mainExecutor
|
||||||
|
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
private val mediaSoundHelper = MediaSoundHelper()
|
private val mediaSoundHelper = MediaSoundHelper()
|
||||||
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
|
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
|
||||||
|
|
||||||
private val orientationEventListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) {
|
private val orientationEventListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) {
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun onOrientationChanged(orientation: Int) {
|
override fun onOrientationChanged(orientation: Int) {
|
||||||
@ -115,7 +123,7 @@ class CameraXPreview(
|
|||||||
init {
|
init {
|
||||||
bindToLifeCycle()
|
bindToLifeCycle()
|
||||||
mediaSoundHelper.loadSounds()
|
mediaSoundHelper.loadSounds()
|
||||||
viewFinder.doOnLayout {
|
previewView.doOnLayout {
|
||||||
startCamera()
|
startCamera()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,7 +151,7 @@ class CameraXPreview(
|
|||||||
val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera initialization failed.")
|
val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera initialization failed.")
|
||||||
val metrics = windowMetricsCalculator.computeCurrentWindowMetrics(activity).bounds
|
val metrics = windowMetricsCalculator.computeCurrentWindowMetrics(activity).bounds
|
||||||
val aspectRatio = aspectRatio(metrics.width(), metrics.height())
|
val aspectRatio = aspectRatio(metrics.width(), metrics.height())
|
||||||
val rotation = viewFinder.display.rotation
|
val rotation = previewView.display.rotation
|
||||||
|
|
||||||
preview = buildPreview(aspectRatio, rotation)
|
preview = buildPreview(aspectRatio, rotation)
|
||||||
val captureUseCase = getCaptureUseCase(aspectRatio, rotation)
|
val captureUseCase = getCaptureUseCase(aspectRatio, rotation)
|
||||||
@ -154,8 +162,8 @@ class CameraXPreview(
|
|||||||
preview,
|
preview,
|
||||||
captureUseCase,
|
captureUseCase,
|
||||||
)
|
)
|
||||||
preview?.setSurfaceProvider(viewFinder.surfaceProvider)
|
preview?.setSurfaceProvider(previewView.surfaceProvider)
|
||||||
setupFocus()
|
setupZoomAndFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupCameraObservers() {
|
private fun setupCameraObservers() {
|
||||||
@ -223,10 +231,12 @@ class CameraXPreview(
|
|||||||
|
|
||||||
private fun getCaptureUseCase(aspectRatio: Int, rotation: Int): UseCase {
|
private fun getCaptureUseCase(aspectRatio: Int, rotation: Int): UseCase {
|
||||||
return if (isPhotoCapture) {
|
return if (isPhotoCapture) {
|
||||||
|
cameraProvider?.unbind(videoCapture)
|
||||||
buildImageCapture(aspectRatio, rotation).also {
|
buildImageCapture(aspectRatio, rotation).also {
|
||||||
imageCapture = it
|
imageCapture = it
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
cameraProvider?.unbind(imageCapture)
|
||||||
buildVideoCapture().also {
|
buildVideoCapture().also {
|
||||||
videoCapture = it
|
videoCapture = it
|
||||||
}
|
}
|
||||||
@ -252,6 +262,7 @@ class CameraXPreview(
|
|||||||
|
|
||||||
private fun buildVideoCapture(): VideoCapture<Recorder> {
|
private fun buildVideoCapture(): VideoCapture<Recorder> {
|
||||||
val recorder = Recorder.Builder()
|
val recorder = Recorder.Builder()
|
||||||
|
//TODO: user control for quality
|
||||||
.setQualitySelector(QualitySelector.from(Quality.FHD))
|
.setQualitySelector(QualitySelector.from(Quality.FHD))
|
||||||
.build()
|
.build()
|
||||||
return VideoCapture.withOutput(recorder)
|
return VideoCapture.withOutput(recorder)
|
||||||
@ -267,30 +278,32 @@ class CameraXPreview(
|
|||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
// source: https://stackoverflow.com/a/60095886/10552591
|
// source: https://stackoverflow.com/a/60095886/10552591
|
||||||
private fun setupFocus() {
|
private fun setupZoomAndFocus() {
|
||||||
|
Log.i(TAG, "camera controller: ${previewView.controller}")
|
||||||
val gestureDetector = GestureDetector(activity, object : SimpleOnGestureListener() {
|
val gestureDetector = GestureDetector(activity, object : SimpleOnGestureListener() {
|
||||||
override fun onSingleTapConfirmed(event: MotionEvent?): Boolean {
|
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
|
||||||
Log.i(TAG, "onSingleTapConfirmed: x=${event?.x}, y=${event?.y}")
|
return camera?.cameraInfo?.let {
|
||||||
return event?.let {
|
val display = displayManager.getDisplay(Display.DEFAULT_DISPLAY)
|
||||||
val factory: MeteringPointFactory = SurfaceOrientedMeteringPointFactory(viewFinder.width.toFloat(), viewFinder.height.toFloat())
|
val width = previewView.width.toFloat()
|
||||||
|
val height = previewView.height.toFloat()
|
||||||
|
Log.i(TAG, "onSingleTapConfirmed: width=$width,height=$height")
|
||||||
|
val factory = DisplayOrientedMeteringPointFactory(display, it, width, height)
|
||||||
val xPos = event.x
|
val xPos = event.x
|
||||||
val yPos = event.y
|
val yPos = event.y
|
||||||
val autoFocusPoint = factory.createPoint(event.x, event.y)
|
val autoFocusPoint = factory.createPoint(xPos, yPos, AF_SIZE)
|
||||||
try {
|
val autoExposurePoint = factory.createPoint(xPos, yPos, AE_SIZE)
|
||||||
val focusMeteringAction = FocusMeteringAction.Builder(autoFocusPoint, FocusMeteringAction.FLAG_AF)
|
val focusMeteringAction = FocusMeteringAction.Builder(autoFocusPoint, FocusMeteringAction.FLAG_AF)
|
||||||
|
.addPoint(autoExposurePoint, FocusMeteringAction.FLAG_AE)
|
||||||
.disableAutoCancel()
|
.disableAutoCancel()
|
||||||
.build()
|
.build()
|
||||||
camera?.cameraControl?.startFocusAndMetering(focusMeteringAction)
|
camera?.cameraControl?.startFocusAndMetering(focusMeteringAction)
|
||||||
listener.onFocusCamera(xPos, yPos)
|
listener.onFocusCamera(xPos, yPos)
|
||||||
Log.i(TAG, "start focus")
|
Log.i(TAG, "start focus")
|
||||||
} catch (e: CameraInfoUnavailableException) {
|
|
||||||
Log.e(TAG, "cannot access camera", e)
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
} ?: false
|
} ?: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
viewFinder.setOnTouchListener { _, event ->
|
previewView.setOnTouchListener { _, event ->
|
||||||
Log.i(TAG, "setOnTouchListener: x=${event.x}, y=${event.y}")
|
Log.i(TAG, "setOnTouchListener: x=${event.x}, y=${event.y}")
|
||||||
gestureDetector.onTouchEvent(event)
|
gestureDetector.onTouchEvent(event)
|
||||||
true
|
true
|
||||||
|
Reference in New Issue
Block a user