Generating and displaying QR codes on Android Wear devices

Source code to accompany this article: https://github.com/bmarrdev/AndroidWearQRGenerator

The introduction of the Android Wear platform created an new challenge for Android developers. In the Android ecosystem developers have long been considering devices with varying form factors, resolutions and screen densities. With Android wear there is now a new form factor unlike those that have come before it, the beautiful circular display that conforms to the traditional watch face and was launched with the Moto 360.

Android wear round

This round form factor throws many curve balls when it comes to UI design, especially with a device small enough to strap to your wrist. This article explains how to dynamically encode data into a square QR code that is optimized for display on either the square or round displays found on Android Wear devices.

If you are not well versed with UI design patterns for Android Wear devices, now is a good time to take a look at building shape aware layouts in the developer documentation.

First a couple of words about QR codes. If you have opened this article and read this far I am assuming you are aware of what QR codes are and and how they are used. What you may not know is that QR codes are resilient, if they are partially obscured or damaged readers can often still decode them successfully. This is due to inbuilt Reed-Solomon error correction. You may have seen customized QR codes that have embedded images and a kaleidoscope of colors and shapes for the data. These work by pushing the boundaries of the error correction and you should be aware that the more changes you make to the QR code data, the more difficult it is for a QR scanner to interpret the data. This article is about generating a QR code that conforms as close as possible to the QR specification to give QR scanners the best possible chance to decode the QR code being displayed on your wearable device.

Optimal QR Code Generation

Below is an example QR code with an overlay of the important elements that make up the data of a QR code.

QR Code structure. By Bobmath [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons

QR Code structure. By Bobmath CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0), via Wikimedia Commons

Your goal is to create a QR code that will provide the most reliable decoding in a range of lighting conditions using poor quality scanning equipment. Some of the characteristics of optimal QR codes are:

  • Colors: Black for data and white for background and quiet zone
  • Quiet Zone: The more blank area in the margin, or the quiet zone (see QR Code structure image above) as it is known, the better
  • Data: The shorter the string to encode the better
  • Display Size: Recommended minimum physical size of 1″ for the data plus space for the quiet zone
  • Resolution: Generate the image with at least 400 pixels wide and high so it can be displayed without scaling artifacts

The image below shows the ideal display of a QR code running on a square wearable device emulator. Once again I will stress the importance of the quiet zone, always leave a blank section around the data.

Android Wear (Square device)

The next image shows a QR code displayed on a round wearable device. When displaying a square QR code on a round canvas you have to compromise. This does not strictly follow the QR specification as the quiet zone around the image does not match the recommended size for the complete edge of the data. It does however allow the data area of the code to be larger and this offers the best chance for a successful decoding of the QR code. For those that don’t want to read the QR specification, the size of the quiet zone should ideally be 4 times the width of the data modules (seen as thin black data lines).

Android Wear (round device)

Now that you have seen what we are trying the achieve I will walk through generating the QR code as an image and how to display the images on each shape wearable.

Generating a QR Code

Generating a QR code from within any android app is quite simple thanks to the excellent library developed by Zxing.

If you are using Eclipse you can import the zxing jar library. If you are using Android Studio and grade, add the following to the build.gradle for your app module.

repositories {
 mavenCentral()
 maven {
     url "https://raw.github.com/embarkmobile/zxing-android-minimal/mvn-repo/maven-repository/"
 }
}

dependencies {
 compile 'com.google.zxing:core:2.2'
 compile 'com.embarkmobile:zxing-android-minimal:1.2.1@aar'
}

Once you have successfully added the zxing library you can generate a bitmap image in memory using the function shown below. You should note that the EncodeHintType.MARGIN allows the quiet zone of the image to be set. Passing a value of 4 as this margin will set the quiet zone to the recommended 4 times the data module size, passing 0 will create an image without any quiet zone.

 /**
 * Allow the zxing engine use the default argument for the margin variable
 */
 static public int MARGIN_AUTOMATIC = -1;

 /**
 * Set no margin to be added to the QR code by the zxing engine
 */
 static public int MARGIN_NONE = 0;

 /**
 * Encode a string into a QR Code and return a bitmap image of the QR code
 *
 * @param contentsToEncode String to be encoded, this will often be a URL, but could be any string
 * @param imageWidth number of pixels in width for the resultant image
 * @param imageHeight number of pixels in height for the resultant image
 * @param marginSize the EncodeHintType.MARGIN parameter into zxing engine
 * @param color data color for QR code
 * @param colorBack background color for QR code
 * @return bitmap containing QR code image
 * @throws WriterException zxing engine is unable to create QR code data
 * @throws IllegalStateException when executed on the UI thread
 */
 static public Bitmap generateBitmap(@NonNull String contentsToEncode,
     int imageWidth, int imageHeight,
     int marginSize, int color, int colorBack)
         throws WriterException, IllegalStateException {

     if (Looper.myLooper() == Looper.getMainLooper()) {
         throw new IllegalStateException("Should not be invoked from the UI thread");
     }

     Map<EncodeHintType, Object> hints = null;
     if (marginSize != MARGIN_AUTOMATIC) {
         hints = new EnumMap<>(EncodeHintType.class);
         // We want to generate with a custom margin size
         hints.put(EncodeHintType.MARGIN, marginSize);
     }

     MultiFormatWriter writer = new MultiFormatWriter();
     BitMatrix result = writer.encode(contentsToEncode, BarcodeFormat.QR_CODE, imageWidth, imageHeight, hints);

     final int width = result.getWidth();
     final int height = result.getHeight();
     int[] pixels = new int[width * height];
     for (int y = 0; y < height; y++) {
         int offset = y * width;
         for (int x = 0; x < width; x++) {
             pixels[offset + x] = result.get(x, y) ? color : colorBack;
         }
     }

     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
     bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
     return bitmap;
 }

Now call the generateBitmap() function and then set the bitmap generated into an ImageView.

Tip: Never generate the QR code on the UI thread, use an AsyncTask, or your android threading method of choice.

final int colorQR = Color.BLACK;
final int colorBackQR = Color.WHITE;
final int width = 400;
final int height = 400;

Bitmap bitmapQR = generateBitmap(mEncodeString, width, height,
                        MARGIN_AUTOMATIC, colorQR, colorBackQR);

Add a layout for displaying the QR code on round wearable devices.

<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_height="match_parent"
 android:layout_width="match_parent"
 tools:context=".MainActivity" tools:deviceIds="wear_round">

   <FrameLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:padding="6dp"
     app:layout_box="all">

     <ImageView
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:id="@+id/imageQR"
         android:layout_gravity="center"
         android:contentDescription="@string/qr_image_description"
         android:visibility="gone" />
  </FrameLayout>
</android.support.wearable.view.BoxInsetLayout>

The key items in the layout are the BoxInsetLayout and the app:layout_box="all" entry. These may be unfamiliar to developers new to Android Wear. BoxInsetLayout places its child view on round screens after applying the required window insets to be placed inside the viewable area of the device, while app:layout_box allows you to set which window insets will be applied.

You could use exactly the same BoxInsetLayout on a square device and it would work as the insets would all be 0. In the example here I have used a different layout simply to show you how to specify a different layout for each device type.

Here is the layout for the rectangular device with a regular relative layout as the base and the image filling the available layout.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
 android:layout_height="match_parent" android:orientation="vertical"
 tools:context=".MainActivity" tools:deviceIds="wear_square">

 <ImageView
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/imageQR"
     android:layout_centerVertical="true"
     android:layout_centerHorizontal="true"
     android:contentDescription="@string/qr_image_description"
     android:visibility="gone" />

</RelativeLayout>

Source Code for this article

Source code for this example can be cloned from the GitHub repository below. You will need an Android Studio development environment set up and the Android Wear SDK installed.

https://github.com/bmarrdev/AndroidWearQRGenerator

References

Android Wear Development

https://developer.android.com/wear

Android Wear layouts

https://developer.android.com/training/wearables/ui/layouts.html

Zxing GitHub home

https://github.com/zxing/zxing

QR Code reference

http://en.wikipedia.org/wiki/QR_code

1 Comment

Filed under Android SDK, Android UI Patterns, Android Wear

One response to “Generating and displaying QR codes on Android Wear devices

  1. WhamBam

    Excellent article, not only did it show me exactly what I wanted to do (dynamically generate a QR code on wearable) but the technical info and tips on optimizing the display according the QR best practices saved me a load of time. Great work!

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s