Integrating QR Codes into an Android App with ZXing

One of the great strengths of the Android mobile platform is that there is a well defined interface for applications to communicate with one-another. This makes it so that functions and features of one application can utilize functions and features of another application to build something new. Very cool stuff!

Together with the power of open source software, this capability makes it possible to add new features to an application in just a few minutes. I'm going to show a small example of how this works using QR codes which are a kind of 2-d barcode that can encode an impressive amount of information, over 4,000 characters. You can generate QR codes yourself if you want to try it out. Here's a QR code that would take you to the Animate Innovations web site:

ZXing (pronounced "Zebra Crossing", which is called Barcode Scanner in the Android Market) is an open source QR code generator and scanner.

The goal of my little project is to be able to pass notes between two mobile phones. This is accomplished by using the camera on one phone to scan the displayed barcode on another phone.

ZXing's startup screen explains it visually:

There is an excellent open source notepad application for the Android called OI Notepad. It has lots of cool features like reading notes from the SD card, sending via email, and even encrypting notes. So let's add the ability to scan a QR code and insert that data into the current note.

The first step was to copy a couple of files from ZXing source code into the OI Notepad source tree. These files aren't strictly necessary, but IntentIntegrator.java offers some helpful code to call the scanner.

Scanning a Barcode:

In OI Notepad, add a menu item for scanning a barcode:

In the NoteEditor.java file, we'll add something like this:

menu.add(1, MENU_BARCODE_INSERT, 0, R.string.menu_barcode_scan)
        .setShortcut('1', 'b'));

Now let's handle barcode scanning: Modify the code that handles the menu click callback to add a call to "initiateScan" which is provided by the IntentIntegrator file in zxing's source code:

    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle all of the possible menu actions.
        switch (item.getItemId()) {
       //...
        case MENU_BARCODE_INSERT:
        	IntentIntegrator.initiateScan(NoteEditor.this);
        	break;
         //...
    }

That call to "initiateScan" invokes an inter-process call in Android. In this case, it launches the BarcodeScanner application, which is a totally different program. Using barcode scanner, the user can now point the camera at a barcode, which is scanned, and sent back to our application in plain text. We have to handle the callback:

    protected void onActivityResult (int requestCode, int resultCode, Intent data) {
    	switch(requestCode) {
       //...
    	case IntentIntegrator.REQUEST_CODE:
    		if (resultCode == RESULT_OK) {
    			IntentResult scanResult = 
                         IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
    	    	if (scanResult != null) {
    	    		String out = scanResult.getContents();
    	    		if (out != null) {
    	    			insertAtPoint (out);
    	    		}
    	    	}
    	}

The core of what we're doing here is scanResult.getContents() which returns the scanned barcode as a string. The function "insertAtPoint" just inserts any String into the current note at the current cursor position.

And that's it! It doesn't take much code to integrate these two applications, and ZXing does a great job of helping out by providing the IntentIntegrator class. However, ZXing does not have any helper functions for generating barcodes, so that'll be slightly more complicated.

Generating QR Codes with ZXing's Intents

Intents are at the core of Android's IPC mechanism. The initiateScan function just called out to an intent under the hood, so we're going to create a similar function for encoding text in a barcode, and we'll add that function to the IntentIntegrator class so that others can use it:

public static void encodeText (Activity activity, String text) {
  	Intent i = new Intent();
  	i.setAction("com.google.zxing.client.android.ENCODE");
  	i.putExtra ("ENCODE_TYPE", "TEXT_TYPE");
  	i.putExtra ("ENCODE_DATA", text);
  	activity.startActivity(i);
}

There are 3 things to do here: Set the action, set the parameters, and start the activity. The action is "ENCODE", and it's fully qualified by the zxing name so that the Android OS knows what application to call to encode that string as a bar code. There are two parameters (or "Extras") one for the type of content, in this case plain text (it could aslo be a URL or a contact), and the data to be encoded.

The result should be something like this:

Since the activity we're calling doesn't actually return any results (unlike the SCAN activity), we don't need to write a callback.

One complication that always comes up with intents is that zxing might not actually be installed on the target system, so in reality, we have to handle an activity not found exception:

  	try {
  		activity.startActivity(i);
  	}catch (ActivityNotFoundException e) {
  		showDownloadDialog(activity, stringTitle, stringMessage, stringButtonYes, stringButtonNo);
  	}

In this case, it's handled by displaying a dialog inviting the user to install the barcode scanner.

Integrating this into the Notepad application is simple. Add a new menu item as before, handle the menu item click, pull the text from the note, and call our encode helper:

IntentIntegrator.encodeText (NoteEditor.this, mText.getText().toString());

Conclusion:
That's all it takes to integrate two Android applications to create a pretty cool new feature: passing notes between two mobile phones with nothing but the camera.