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:
darthpaul
2022-06-25 21:42:15 +01:00
parent e15078499b
commit b44b6a9b2b

View File

@ -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)
.disableAutoCancel() .addPoint(autoExposurePoint, FocusMeteringAction.FLAG_AE)
.build() .disableAutoCancel()
camera?.cameraControl?.startFocusAndMetering(focusMeteringAction) .build()
listener.onFocusCamera(xPos, yPos) camera?.cameraControl?.startFocusAndMetering(focusMeteringAction)
Log.i(TAG, "start focus") listener.onFocusCamera(xPos, yPos)
} catch (e: CameraInfoUnavailableException) { Log.i(TAG, "start focus")
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