takePicture require executor on CameraX (1.0.0-alpha06)

The official Google Codelabs which obviously have been updated recently use: Executors.newSingleThreadExecutor()

Reference: https://codelabs.developers.google.com/codelabs/camerax-getting-started/#4

Edit: Since @kos's response also makes sense to me, I've added these two official Android docs references:

https://developer.android.com/reference/java/util/concurrent/Executors.html#newSingleThreadExecutor()

https://developer.android.com/reference/java/util/concurrent/Executors.html#newCachedThreadPool()

This way every reader of this topic can make up his/her own mind with respect to executors.

FURTHER EDIT: There are crucial API changes since 1.0.0-alpha07 so I studied some of the docs. There's a GitHub sample showing executor retrieval like so mainExecutor = ContextCompat.getMainExecutor(requireContext())(Source)

If some of you already implemented CameraX and it works fine, I'd definitely wait for the beta release as recommended by Android's release notes


You can do it like this.

imageCapture.takePicture(file, { it.run() }, object : ImageCapture.OnImageSavedListener {
    override fun onImageSaved(file: File) {}
    override fun onError(useCaseError: ImageCapture.ImageCaptureError, message: String, cause: Throwable?) {}
})

I faced same thing as you are facing. I resolved it from my side.

class MainActivity : AppCompatActivity(), Executor {
    private var right: Int = 0
    private var bottom: Int = 0
    private var left: Int = 0
    private var top: Int = 0
    private lateinit var preview: Preview
    private val REQUEST_CODE_PERMISSIONS = 10
    private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    private lateinit var imageCapture: ImageCapture
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (allPermissionsGranted()) {
            viewFinder.post { startCamera() }
        } else {
            ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }

        viewFinder.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
            updateTransform()
        }

        buttonPlus.setOnClickListener {
            if (right < 100) {
                right += 100
                bottom += 100
                left += 100
                top += 100
                val my = Rect(left, top, right, bottom)
                preview.zoom(my)
            }
        }

        buttonMinus.setOnClickListener {
            if (right > 0) {
                right -= 100
                bottom -= 100
                left -= 100
                top -= 100
                val my = Rect(left, top, right, bottom)
                preview.zoom(my)
            }
        }
    }

    @SuppressLint("RestrictedApi")
    private fun startCamera() {
        val metrics = DisplayMetrics().also { viewFinder.display.getRealMetrics(it) }
        val screenAspectRatio = Rational(metrics.widthPixels, metrics.heightPixels)
        val previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatioCustom(screenAspectRatio)
            setTargetRotation(viewFinder.display.rotation)
        }.build()
        preview = Preview(previewConfig)
        preview.setOnPreviewOutputUpdateListener {
            val parent = viewFinder.parent as ViewGroup
            parent.removeView(viewFinder)
            parent.addView(viewFinder, 0)
            viewFinder.surfaceTexture = it.surfaceTexture
            updateTransform()
        }
        CameraX.bindToLifecycle(this, preview)

        captureImage()
    }

    @SuppressLint("RestrictedApi")
    private fun captureImage() {
        val imageCaptureConfig = ImageCaptureConfig.Builder()
                .apply {
                    setTargetAspectRatioCustom(Rational(1, 1))
                    setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
                }.build()
        imageCapture = ImageCapture(imageCaptureConfig)
        CameraX.bindToLifecycle(this, imageCapture)
        capture_button.setOnClickListener {
            val file = File(this.externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
            imageCapture.takePicture(file, this, object : ImageCapture.OnImageSavedListener {
                override fun onImageSaved(file: File) {
                    val msg = "Photo capture succeeded: ${file.absolutePath}"
                    Log.d("CameraXApp", msg)
                }

                override fun onError(imageCaptureError: ImageCapture.ImageCaptureError, message: String, cause: Throwable?) {
                    val msg = "Photo capture failed: $message"
                    Log.e("CameraXApp", msg)
                    cause?.printStackTrace()
                }
            })
        }
    }

    override fun execute(command: Runnable) {
        command.run()
    }

    private fun updateTransform() {
        val matrix = Matrix()
        val centerX = viewFinder.width / 2f
        val centerY = viewFinder.height / 2f
        val rotationDegrees = when (viewFinder.display.rotation) {
            Surface.ROTATION_0 -> 0
            Surface.ROTATION_90 -> 90
            Surface.ROTATION_180 -> 180
            Surface.ROTATION_270 -> 270
            else -> return
        }
        matrix.postRotate(-rotationDegrees.toFloat(), centerX, centerY)
        viewFinder.setTransform(matrix)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                viewFinder.post { startCamera() }
            } else {
                Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
                finish()
            }
        }
    }

    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
    }


    override fun onDestroy() {
        super.onDestroy()
        imageCapture.let {
            CameraX.unbind(imageCapture)
        }
    }
}

And the output is (As I print log in onImageSaved method)

Photo capture succeeded: /storage/emulated/0/Android/media/com.akshay.cameraxzoominoutdemo/1571052301192.jpg

It's working fine for me, try out this.