This year at the Android Dev Summit Google released the first canary of Android Studio 4.0. One of the biggest changes is the built-in support for Jetpack Compose, a declarative UI toolkit for building native Android UI. With this new update for Android Studio, developers can now easily experiment with the new Jetpack Compose libraries in their apps.

One of the main selling points of Compose is its ability to work alongside existing Android UI components, allowing you to adopt Compose components into your app at your own pace. In this blog post we will build a sample bookshelf app with Compose alongside the PDFTron document viewer.

Please note that Jetpack Compose is currently in Developer Preview and should not be used in production apps.

linkBuild UI with Composable Functions

Compose is built around using composable functions, called composables, that define your app's UI. To create a composable, add the @Composable annotation to a function. Let's start by creating the bookshelf that contains the document thumbnails.

First we'll create a data class that will hold our collection of documents.

@Model
class BookshelfState() {
    val documents =
        listOf(
            "contract.pdf",
            "sample.pdf",
            "blueprint.pdf",
            "drawing.pdf",
            "floorplan.pdf",
            "formula.pdf",
            "invoice.pdf",
            "music.pdf",
            "news.pdf"
        )
}

In terms of data flow, we'll use a top-down data approach where the document data is passed in from upper-level composables to its children. We'll pass this data to a BookshelfItem composable which will contain a thumbnail preview and title of the document.

@Composable
fun BookshelfItem(docName: String) {
    val context = +ambient(ContextAmbient)
    val thumbnail = imageFromResource(
        context.resources,
        getThumbnailResourceFromString(context, docName)!!
    )
    Padding(4.dp) {
        Card(shape = RoundedCornerShape(4.dp), elevation = 2.dp) {
            Column(
                crossAxisAlignment = CrossAxisAlignment.Center,
                crossAxisSize = LayoutSize.Wrap
            ) {
                SimpleImage(thumbnail)
                Padding(8.dp) {
                    Text(
                        text = docName,
                        style = (+themeTextStyle { subtitle1 })
                    )
                }
            }
        }
    }
}

Then we'll put each BookshelfItem into a 3-column Table for better viewing.

@Composable
private fun Bookshelf(bookshelfState: BookshelfState) {
    val columns = 3
    Table(
        columns = columns
    ) {
        val groupedDocs = bookshelfState.documents.chunked(columns)
        groupedDocs.forEach {
            this.tableRow {
                it.forEach {
                    BookshelfItem(it)
                }
            }
        }
    }
}

Finally, we can add this to the root view of our MainActivity.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val documentsState = BookshelfState();
        setContent {
            MaterialTheme {
                Bookshelf(documentsState)
            }
        }
    }
}

The app should now look something like this:

screenshot

linkDisplay Documents Using PDFTron

As mentioned previously, one of the main features of Compose is the ability to use composables alongside existing Android components such as views, fragments, and activities. We can can easily launch the PDFTron document viewer in our app with a couple lines of code.

@Composable
fun ClickableBookshelfItem(docName: String) {
    val context = +ambient(ContextAmbient)
    val config = ViewerConfig.Builder()
        .multiTabEnabled(false)
        .build();

    // Wrap bookshelf item with a clickable composable
    // that will launch the viewer
    Ripple(bounded = true) {
        Clickable(onClick = {
            // Launch the viewer for the clicked document
            DocumentActivity.openDocument(
                context,
                getResourceFromString(context, docName),
                config
            );
        }) {
            BookshelfItem(docName)
        }
    }
}

Here we're able to reuse the BookshelfItem composable to add a ripple effect and click functionality. In our app, we can substitute BookshelfItem with ClickableBookshelfItem in the bookshelf composable so that clicking on thumbnails will display the PDF document.

Bookshelf

linkConclusion

With Jetpack Compose comes a new paradigm for Android UI development. It simplifies and accelerates UI development with less code and powerful tools. We're excited to see what's in store for the future of Android development and we look forward to working with Compose as the framework matures.

If you have any questions about PDFTron's Android PDF SDK, please feel free to get in touch!

You can find the source code for this blog post at Github.