17

I've followed the steps here to get CameraX setup, and now I am trying to get a front facing camera button working.

Here is my set up code:

private lateinit var preview: Preview

private fun startCamera() {

    // Create configuration object for the viewfinder use case
    val previewConfig = PreviewConfig.Builder().apply {
        setLensFacing(CameraX.LensFacing.BACK)
    }.build()

    // Build the viewfinder use case
    preview = Preview(previewConfig)

    // Every time the viewfinder is updated, recompute layout
    preview.setOnPreviewOutputUpdateListener {

        // To update the SurfaceTexture, we have to remove it and re-add it
        val parent = viewFinder.parent as ViewGroup
        parent.removeView(viewFinder)
        parent.addView(viewFinder, 0)

        viewFinder.surfaceTexture = it.surfaceTexture
        updateTransform()
    }

    // Bind use cases to lifecycle
    CameraX.bindToLifecycle(this, preview)
}

When a user clicks the "switch" button I re-configure the preview to use the front camera, then reinitialize the Preview.

private fun initSwitchButton(view: View) {
    switchButton = view.findViewById(R.id.switch_button)
    switchButton.setOnClickListener {
        val previewConfig = PreviewConfig.Builder().apply { setLensFacing(CameraX.LensFacing.FRONT) }.build()
        preview = Preview(previewConfig)
    }
}

However, this doesn't switch to the front camera. What am I missing?

Andrew Gable
  • 2,692
  • 2
  • 24
  • 36
  • Since you are creating a new `Preview` instance, wouldn't you need the `setOnPreviewOutputUpdateListener` and `bindToLifecycle()` bits from your first code snippet to be run on this new `Preview`? (in addition to cleaning up the old `Preview` instance, if that wasn't done already) I haven't used CameraX yet, so I may be off-base -- this is just comparing and contrasting your two code snippets. – CommonsWare May 16 '19 at 23:12
  • Just found an example, https://github.com/android/camera/blob/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt#L341-L355 and it does look like you need to call `bindToLifecycle` once again. I will update with an answer when I find a clean solution! – Andrew Gable May 16 '19 at 23:35

4 Answers4

20

Since 2021, an update to CameraX has rendered CameraX.LensFacing unusable. Use CameraSelector instead.

    private CameraSelector lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;

    private void flipCamera() {
        if (lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA) lensFacing = CameraSelector.DEFAULT_BACK_CAMERA;
        else if (lensFacing == CameraSelector.DEFAULT_BACK_CAMERA) lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;
        startCamera();
    }

    private void startCamera() {
        ListenableFuture<ProcessCameraProvider> cameraFuture = ProcessCameraProvider.getInstance(requireContext());

        cameraFuture.addListener(() -> {
            imageCapture = new ImageCapture.Builder()
                .setTargetRotation(cameraPreview.getDisplay().getRotation())
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build();
            videoCapture = new VideoCapture.Builder().build();

        try {
            ProcessCameraProvider processCameraProvider = cameraFuture.get();
            Preview preview = new Preview.Builder().build();
            preview.setSurfaceProvider(cameraPreview.getSurfaceProvider());
            processCameraProvider.unbindAll(); 

            // lensFacing is used here
            processCameraProvider.bindToLifecycle(getViewLifecycleOwner(), lensFacing, imageCapture, videoCapture, preview);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        }, ContextCompat.getMainExecutor(requireContext()));
    }
Rig
  • 301
  • 3
  • 5
10

It looks like the recommended way to achieve this is to store the LensFacing position as an instance variable and then call bindToLifecycle() to switch the camera.

Here is a code snippet that worked for me:

private var lensFacing = CameraX.LensFacing.BACK
private var imageCapture: ImageCapture? = null

@SuppressLint("RestrictedApi")
private fun startCamera() {
    bindCameraUseCases()

    // Listener for button used to switch cameras
    switchButton = view.findViewById(R.id.switch_button)
    switchButton.setOnClickListener {
        lensFacing = if (CameraX.LensFacing.FRONT == lensFacing) {
            CameraX.LensFacing.BACK
        } else {
            CameraX.LensFacing.FRONT
        }
        try {
            // Only bind use cases if we can query a camera with this orientation
            CameraX.getCameraWithLensFacing(lensFacing)
            bindCameraUseCases()
        } catch (exc: Exception) {
            // Do nothing
        }
    }
}

private fun bindCameraUseCases() {
    // Make sure that there are no other use cases bound to CameraX
    CameraX.unbindAll()

    val previewConfig = PreviewConfig.Builder().apply {
        setLensFacing(lensFacing)
    }.build()
    val preview = Preview(previewConfig)

    val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
        setLensFacing(lensFacing)
    }.build()
    imageCapture = ImageCapture(imageCaptureConfig)

    // Apply declared configs to CameraX using the same lifecycle owner
    CameraX.bindToLifecycle(this, preview, imageCapture)
}
Andrew Gable
  • 2,692
  • 2
  • 24
  • 36
1
private LensFacing lensFacing = CameraX.LensFacing.BACK;
private ImageCapture imageCapture = null;
private Button switchButton;


@SuppressLint("RestrictedApi")
private void startCamera() {
    bindCameraUseCases();

    // Listener for button used to switch cameras
    switchButton = view.findViewById(R.id.switch_button);
    switchButton.setOnClickListener(v -> {
        lensFacing = lensFacing == LensFacing.FRONT ? LensFacing.BACK : LensFacing.FRONT;
        try {
            // Only bind use cases if we can query a camera with this orientation
            CameraX.getCameraWithLensFacing(lensFacing);
            bindCameraUseCases();
        } catch (CameraInfoUnavailableException e) {
            // Do nothing
        }
    });
}

private void bindCameraUseCases() {
    // Make sure that there are no other use cases bound to CameraX
    CameraX.unbindAll();

    PreviewConfig previewConfig = new PreviewConfig.Builder().
            setLensFacing(lensFacing)
            .build();
    Preview preview = new Preview(previewConfig);

    ImageCaptureConfig imageCaptureConfig = new ImageCaptureConfig.Builder()
            .setLensFacing(lensFacing)
            .build();
    imageCapture = new ImageCapture(imageCaptureConfig);

    // Apply declared configs to CameraX using the same lifecycle owner
    CameraX.bindToLifecycle(this, preview, imageCapture);
}

Java version

Renegade
  • 642
  • 1
  • 10
  • 19
0

Here is how i did mine

private var defaultCameraFacing = CameraSelector.DEFAULT_BACK_CAMERA

   btnFlipCamera.setOnClickListener {
        Log.d("CameraFacing", defaultCameraFacing.toString())
        defaultCameraFacing = if(defaultCameraFacing == CameraSelector.DEFAULT_FRONT_CAMERA){
            CameraSelector.DEFAULT_BACK_CAMERA
        }else{
            CameraSelector.DEFAULT_FRONT_CAMERA
        }

        try {
            // Only bind use cases if we can query a camera with this orientation
            startCamera(defaultCameraFacing)
        } catch (exc: Exception) {
            // Do nothing
        }
    }

private fun startCamera(defaultCameraFacing: CameraSelector) {
    llPictureCaptured.visibility = View.GONE
    tvLocationLabel.visibility= View.GONE
    pgLoadingLocation.visibility = View.GONE
    openCamera.visibility = View.GONE
    llCameraControl.visibility = View.VISIBLE
    viewFinder.visibility = View.VISIBLE


    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        // Used to bind the lifecycle of cameras to the lifecycle owner
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

        // Preview
        val preview = Preview.Builder()
            .build()
            .also {
                it.setSurfaceProvider(viewFinder.surfaceProvider)
            }

        imageCapture = ImageCapture.Builder()
            .build()

        //set image analysis, i.e luminosity analysis
        val imageAnalyzer = ImageAnalysis.Builder()
            .build()
            .also {
                it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
                    Log.d(TAG, "Average luminosity: $luma")
                })
            }

        // Set camera facing
        val cameraSelector = defaultCameraFacing

        try {
            // Unbind use cases before rebinding
            cameraProvider.unbindAll()

            // Bind use cases to camera
            cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, imageCapture, imageAnalyzer)

        } catch (exc: Exception) {
            Log.e(TAG, "Use case binding failed", exc)
        }

    }, ContextCompat.getMainExecutor(this))
}
Sterlingking
  • 190
  • 1
  • 6