CameraX: Getting Started

Learn how to implement camera features on Android using CameraX library By Tino Balint.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Adding Click Listeners

Next, you’ll add a click listener so that you can click on the preview and take a picture. Go back to onCreate and replace setClickListeners with this:

  private fun setClickListeners() {
    toggleCameraLens.setOnClickListener { toggleFrontBackCamera() }
    previewView.setOnClickListener { takePicture() }
  }

Build and run. Click on the preview now to take a picture. Sweet!

app screenshot with same picture of a painting in both upper and lower halves

Once you take a picture, you can compare it with the live preview, like this picture of a picture. :]

Storing Images to File

Congratulations, your app can take pictures! But what if you want to save them?

You can do that with this updated version of savePictureToFile. Replace the currently empty method with this:

  private fun savePictureToFile() {
    // 1
    fileUtils.createDirectoryIfNotExist()
    val file = fileUtils.createFile()

    // 2
    imageCapture?.takePicture(file, getMetadata(), executor,
        object : ImageCapture.OnImageSavedListener {
          override fun onImageSaved(file: File) {
            // 3
            runOnUiThread {
              takenImage.setImageURI(FileProvider.getUriForFile(this@PhotoActivity,
                  packageName,
                  file))
              enableActions()
            }
          }

          override fun onError(imageCaptureError: ImageCapture.ImageCaptureError,
                               message: String,
                               cause: Throwable?) {
            // 4
            Toast.makeText(this@PhotoActivity,
                getString(R.string.image_capture_failed),
                Toast.LENGTH_SHORT).show()
          }
        })
  }

Here’s what you are doing in savePictureToFile:

  1. Create a destination directory and file using the helper methods in the Pictures/Photo_session directory on your device.
  2. Call takePicture with three parameters: The newly-created file to save the image, the metadata for the image and an executor to define which thread to call these methods on. Metadata stores the geographical location of the photo. It also indicates whether the system needs to render the image mirrored horizontally or vertically. For instance, isReversedHorizontal will be true for a selfie.
  3. After taking the photo, if there are no errors, you set the image to ImageView and enable user actions. Notice that you set the image using URI instead of the bitmap. You do this because you have a file object.
  4. If there are any errors, you show them to the user as a Toast.

Build and run to test this new feature. Set the Save Image switch to save.

Picture of painting in full-screen

Take a photo. Now look in the Images/Photo_session directory on your device. Notice something? The image looks very different than the one in your app. That’s because you’re seeing it full-screen. You don’t want people leaving your app to do this so you’ll add this feature now.

Replace setClickListeners with the following:

  private fun setClickListeners() {
    toggleCameraLens.setOnClickListener { toggleFrontBackCamera() }
    previewView.setOnClickListener { takePicture() }
    takenImage.setOnLongClickListener {
      showImagePopup()
      return@setOnLongClickListener true
    }
  }

In this new version of setClickListeners, you add another listener that runs showImagePopup when the user long-clicks on the most recent photo. saveImagePopup is already in the starter project.

Build and run. Take a picture and then long-click on it. It’s full-screen now in its normal aspect ratio and size. Click anywhere again and you’ll return to split-view.

Take a moment to appreciate what you’ve created here!

Picture of painting in full-screen

Adding Vendor Extensions

There’s one more feature you want to add to your camera app: What if your users want to add special effects to their photos like Night Mode or Bokeh?

No problem. With CameraX, you can add these features with only a few lines of code.

  • Huawei (HDR, Portrait): Mate 20 series, P30 series, Honor Magic 2, Honor View 20.
  • Samsung (HDR, Night, Beauty, Auto): Galaxy Note 10 series.
Note: Although many devices have these effects available in the default camera app, they do not necessarily support vendor extensions. Currently, only the following models support this feature:

Add this code below savePictureToMemory:

  private fun enableExtensionFeature(
    imageCaptureExtender: ImageCaptureExtender
  ) {
    if (imageCaptureExtender.isExtensionAvailable) {
      imageCaptureExtender.enableExtension()
    } else {
      Toast.makeText(this, getString(R.string.extension_unavailable),
          Toast.LENGTH_SHORT).show()
      extensionFeatures.setSelection(0)
    }
  }

In this method, you’re enabling an extension, if it’s available. Otherwise, you notify the user that it isn’t available and remove the option from the drop-down menu.

enableExtensionFeature needs to know the image capture extender you want to use. You’ll provide that with the method below:

    private fun applyExtensions(
      builder: ImageCaptureConfig.Builder
    ) {
      when (ExtensionFeature.fromPosition(extensionFeatures.selectedItemPosition)) {
        ExtensionFeature.BOKEH ->
          enableExtensionFeature(BokehImageCaptureExtender.create(builder))
        ExtensionFeature.HDR ->
          enableExtensionFeature(HdrImageCaptureExtender.create(builder))
        ExtensionFeature.NIGHT_MODE ->
          enableExtensionFeature(NightImageCaptureExtender.create(builder))
        else -> {
        }
      }
    }

And add these to your import statements above:

  import androidx.camera.extensions.HdrImageCaptureExtender
  import androidx.camera.extensions.BokehImageCaptureExtender
  import androidx.camera.extensions.NightImageCaptureExtender

In the method above, you check the currently-selected item from the drop-down menu and then create an image extender based on that option.

Listening for the Image Extension

Now that you’ve created this array of image extenders, you’ll add a listener for the drop-down menu.

Go back to onCreate and replace setClickListeners with this:

  private fun setClickListeners() {
    toggleCameraLens.setOnClickListener { toggleFrontBackCamera() }
    previewView.setOnClickListener { takePicture() }
    takenImage.setOnLongClickListener {
      showImagePopup()
      return@setOnLongClickListener true
    }

    extensionFeatures.onItemSelectedListener =
        object : AdapterView.OnItemSelectedListener {
          override fun onItemSelected(
              parentView: AdapterView<*>,
              selectedItemView: View,
              position: Int,
              id: Long
          ) {
            if (ExtensionFeature.fromPosition(position) != ExtensionFeature.NONE) {
              previewView.post { startCamera() }
            }
          }

          override fun onNothingSelected(parentView: AdapterView<*>) {}
        }
  }

Now, when the user selects an available extension, you’ll start the camera — which will automatically apply the extensions.

The only thing left is to call applyExtensions from within createCaptureUseCase. Add the call right before the return:

  private fun createCaptureUseCase(): ImageCapture {
    val imageCaptureConfig = ImageCaptureConfig.Builder()
        .apply {
          setLensFacing(lensFacing)
          setTargetRotation(previewView.display.rotation)
          setCaptureMode(ImageCapture.CaptureMode.MAX_QUALITY)
        }

    applyExtensions(imageCaptureConfig)
    return ImageCapture(imageCaptureConfig.build())
  }

Build and run. The extensions are now available from the drop-down menu on any of the supported devices. Note that the app only applies the extensions to the most recent photo.

Where to Go From Here?

Download the final project using the Download Materials button at the top or the bottom of the tutorial.

In this tutorial, you learned how to use CameraX to preview, take and store photos. You also saw how this library simplifies the process of adding options for special effects.

If you want to learn more, check out the official CameraX documentation.

I hope you enjoyed this tutorial! If you have any questions or comments, please join the forum discussion below.