Integrating ZXing Android Embedded in a Compose app
Tags: android, jetpackcompose, kotlin
Published: 2021-11-14 10:18:34

Jetpack Compose is the preferred UI framework for new Android apps. Its declarative programming model makes writing beautiful user interfaces a breeze. But what if you want to re-use existing code that relies on the traditional `View` system? Through the years countless wonderful custom components have been developed. There may be Compose versions of them some day. But, no need to wait. Jetpack Compose includes powerful yet easy to use interoperability apis. 

In this short article, I show you how to integrate ZXing Android Embedded in a Compose app. The sample app is part of my upcoming book **Android UI Development with Jetpack Compose**. It will be published by Packt and should be available early 2022. You can find the source code on [GitHub](https://github.com/PacktPublishing/Android-UI-Development-with-Jetpack-Compose/tree/main/chapter_09/ZxingDemo).

![Screenshot of the ZxingDemo app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g5j3laqd3peuama7y4kg.png)

*ZxingDemo* uses the **ZXing Android Embedded** Barcode scanner library for Android, which is based on the ZXing decoder. It is released under the terms of the Apache-2.0 License, and is hosted on [GitHub](https://github.com/journeyapps/zxing-android-embedded). 

The aim of *ZximgDemo* is to showcase the integration of `View`s in a Compose app, not to really provide a useful app. It uses ZXing Android Embedded to continuously scan Barcodes and QR-Codes and prints the result in screen. 

To use the library, just add it as an implementation dependency to your module-level *build.gradle* file.

```groovy
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
```

We need to access the camera, so we need to request permissions. Here's the preferred way to do so:

```kotlin
private lateinit var barcodeView: DecoratedBarcodeView

private val requestPermission =
  registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
    if (isGranted) {
      barcodeView.resume()
    }
  }

override fun onResume() {
  super.onResume()
  requestPermission.launch(Manifest.permission.CAMERA)
}

override fun onPause() {
  super.onPause()
  barcodeView.pause()
}
```

`ActivityResultContracts.RequestPermission` replaces the process around overriding `onRequestPermissionsResult()`.

`barcodeView` is initialized in `onCreate()`. Let's take a look.

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  val beepManager = BeepManager(this)
  val root = layoutInflater.inflate(R.layout.layout, null)
  barcodeView = root.findViewById(R.id.barcode_scanner)
  val formats = listOf(BarcodeFormat.QR_CODE, BarcodeFormat.CODE_39)
  barcodeView.barcodeView.decoderFactory = DefaultDecoderFactory(formats)
  barcodeView.initializeFromIntent(intent)
  val callback = object : BarcodeCallback {
    override fun barcodeResult(result: BarcodeResult) {
      if (result.text == null || result.text == text.value) {
        return
      }
      text.value = result.text
      beepManager.playBeepSoundAndVibrate()
    }
  }
  barcodeView.decodeContinuous(callback)
  setContent {
    val state = text.observeAsState()
    state.value?.let {
      ZxingDemo(root, it)
    }
  }
}
```

`barcodeView` references a child element inside the component tree I inflated using `layoutInflater.inflate()`, and assigned to `root`. The layout (`R.layout.layout` represents *layout.xml*) is very simple:

```xml
<?xml version="1.0" encoding="utf-8"?>
<com.journeyapps.barcodescanner.DecoratedBarcodeView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/barcode_scanner"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_alignParentTop="true" />
```

So, the barcode view is provided by `DecoratedBarcodeView` as one of its children. Once we have obtained a reference to the child, we configure it. You can find more information on the in the ZXing Android Embedded documentation.

The Compose-specific part happens inside `setContent {}`.

- We create state using `observeAsState()`
- We invoke a composable named `ZxingDemo()` and pass the value of the state, and `root`

`text` is defined like this:

```kotlin
private val text = MutableLiveData("")
```

It is updated inside `callback` when the scanner engine has provided a result.

Before we look at `ZxingDemo()`, let's briefly recap:

- `root` represents the scanner ui
- When the scanner has a result, it updates `text` (a `MutableLiveData` instance
- `ZxingDemo()` receives the value of a state based on `text` and the root of the scanner ui

Now it's time to see how the integration is achieved:

```kotlin
@Composable
fun ZxingDemo(root: View, value: String) {
  Box(
    modifier = Modifier.fillMaxSize(),
    contentAlignment = Alignment.TopCenter
  ) {
    AndroidView(modifier = Modifier.fillMaxSize(),
      factory = {
        root
      })
    if (value.isNotBlank()) {
      Text(
        modifier = Modifier.padding(16.dp),
        text = value,
        color = Color.White,
        style = MaterialTheme.typography.h4
      )
    }
  }
}
```

We define a `Box()` with two children, `AndroidView()` and `Text()`. `AndroidView()` receives a factory, which just returns `root` (the scanner ui). The docs [say](https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidView(kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1)):

> Composes an Android `View` obtained from `factory`. The `factory` block will be called exactly once to obtain the `View` to be composed, and it is also guaranteed to be invoked on the UI thread. Therefore, in addition to creating the `factory`, the block can also be used to perform one-off initializations and View constant properties' setting. 

You may be wondering why I inflate the object tree in `onCreate()` instead of inside the `factory` lambda. Well, configuring the barcode scanner should not be done in a composable because it might be inherently (preparing camera and preview, ...). Also, parts of the the component tree are accessed from the outside (on the activity level), so we need references to children anyway (`barcodeView`. 

You could also provide an `update` block, which my example doesn't. It runs right after the `factory` block completes, and it can run multiple times due to recomposition. You can use it to set `View` properties, depending on state.

The Compose part of my app does not alter state which needs to be applied to the scanner component tree.

## Conclusion

As *ZxingDemo* shows, it is very easy to integrate libraries that provide custom components. You you need to structure your code depends on how the library is setup and configured. Do you plan to use the Jetpack Compose interop apis? Please share your thoughts in the comments.