Homework 2 – Zebrary book scanner

Zebrary is a simple book-cataloging application that uses the camera of a device and an open-source barcode image processing library called ZXing (pronounced “Zebra Crossing”). The camera is to scan the barcode of a book, ZXing reads the ISBN number, and the Google Books API is used to gather bibliographic information about the volume, including a cover image URL.

Books are kept in an alphabetical list in the main Activity, and touching a list item drills down into a book detail view Activity that displays the author, title, cover image, a couple of links to Worldcat and Amazon for that specific book, and a button to scan a new book. Users can delete a book from the detail view or by long-touching an item in the list.

Using the ZXing Library

The setup for this took a few steps. First, you have to checkout source for /core and /android from the ZXing svn repository, build the /core project using ant, import the /android project into Eclipse and mark it as a Library project. After all this, you can use the ZXing /android project as a library in your own project. Damian Flannery has put together a very nice step-by-step blog post for doing this.

Once the environment is set up, you can launch an Intent based on Activities defined in the ZXing source. For example, the following launches a new SCAN Activity, brining up the camera

This sample code only displays a Toast message. The real code first checks the local SQLite database for a book matching that ISBN. If it finds a match, the user is taken to that book’s detail view. If not, we put together a URL string and issue an HTTP GET request:

The results are used to create a new Book object and add its details to the local database.

Parsing JSON results from a web service

Once you have an API key, the Google Books API provides conveniently-wrapped bibliographic results in JSON format. If you’re never used JSON, it stands for JavaScript Object Notation and is a handy way of bundling up simple data into hierarchical key-value pairs, where the values can be strings, JSON objects, or lists of strings or JSON objects. Java has a built-in JSONObject class that provides methods for easy parsing. A sample JSON result from Google Books might look like this:

The real results have more layers of hierarchy and a lot more data, but this shows enough to get the idea. If we read the full result of the HTTP GET request into a string, we can use JSONObjects as follows:

JSONObject jObject = null;
String title, author, coverImageURLString;
try {
// Create a JSONObject from the text returned by the HTTP GET request.
jObject = new JSONObject(text);
// "title" is a key in the main, first-level JSONObject
title = jObject.getString("title"); // title = "Visualizing Data"
// "authors" is an array (i.e., enclosed in []), and we want the 0th element of that array
author = jObject.getJSONArray("authors").getString(0); // author = "Ben Fry"
// "imageLinks" is the key for a JSONObject inside the main JSONObject, and "thumbnail" is a key inside that
coverImageURLString = jObject.getJSONObject("imageLinks").getString("thumbnail");
}

Reading and writing images

With that, I create a Book object, insert it into the database, and write the cover image to a file. I do this in a lazy way, and BitmapFactory comes in handy.

Controlling the flow of Intents

One problem I came across was the difference in flow between a new book scan originated from the list view and one originated from a book detail view. In both cases, when the user taps back from the new book’s detail view Activity, I wanted them to return to the list view. A number of flags can be set on an Intent using Intent.setFlags(), including FLAG_ACTIVITY_CLEAR_TOP, which pops the launching Activity off the stack, so to speak, before starting the new intent.

Intent intent = new Intent(this, BookDetailViewActivity.class);
intent.putExtra("id", book.getId());
// Setting the FLAG_ACTIVITY_CLEAR_TOP flag means the user won't return to
// this Activity, but rather, to whatever Activity started this one
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

Comments

This is awesome! Great idea for an app, and very useful code comments. I’m a huge fan of JSON – haven’t used it much Android but I have in JavaScript and some other Java projects. I assume this is the Apache JSON libraray?