Tuesday, August 7, 2012

Android - How to write a simple barcode scanner or a QR scanner


Integrating a barcode scanner to your android application is simpler than in other platforms.

There are two ways you can get your android app the ability to scan barcodes.
1. Sending intents to the barcode scanner app - The simplest way
2. Using google source for Zxing - For those who can easily crunch huge volume of code

As the post title says "Simple barcode / QR code scanner" I will discuss the first method here

1. Sending intent for starting the scan

We have to use the intents provided by the barcode scanner app and start the activity for result using startActivityForResult

           Intent intentScan = new Intent("com.google.zxing.client.android.SCAN");  
           intentScan.addCategory(Intent.CATEGORY_DEFAULT);  
   
           // If only QR code should be scanned  
           //intentScan.putExtra("SCAN_FORMATS", "QR_CODE");  
   
           try {  
                this.startActivityForResult(intentScan, MY_REQUEST_CODE);  
           } catch (ActivityNotFoundException e) {  
                downloadFromMarket();  
           }  

If you don't specify any "SCAN_FORMATS" as intent extras, it will scan for all the supported formats (QR_CODE,UPC_A,UPC_E,EAN_8,EAN_13,CODE_39,CODE_93,CODE_128 )
You can speficy only the formats you want by comma separated strings. Example:

 intentScan.putExtra("SCAN_FORMATS", "QR_CODE,UPC_A,UPC_E");  

2. Handling the onActivityResult

Retrieve the "SCAN_RESULT" and "SCAN_RESULT_FORMAT" from the result. In the below code, both the data are extracted and set to a textview.
Make sure you access the intent only when resultCode == Activity.RESULT_OK

      protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
   
           if (requestCode == MY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {  
                String contents = data.getStringExtra("SCAN_RESULT");  
                String formatName = data.getStringExtra("SCAN_RESULT_FORMAT");  
                  
                TextView tv = (TextView)findViewById(R.id.tvResult);  
                tv.setText("Format: " + formatName+ " Code: "+contents);  
           }  
      }  

3. The Barcode scanner app 

The Barcode scanner app from zxing is opensource and you can download the source code form google code. But here we are interested only in the final application.
   
If the barcode scanner app is not present in the device, we can make the user to download it when startActivityForResult() fails with ActivityNotFoundException

      public void downloadFromMarket() {  
           AlertDialog.Builder downloadDialog = new AlertDialog.Builder(this);  
           downloadDialog.setTitle("Warning");  
           downloadDialog.setMessage("Barcode app not found. Download?");  
           downloadDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialogInterface, int i) {  
                     Uri uri = Uri.parse("market://search?q=pname:com.google.zxing.client.android");  
                     Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
                     startActivity(intent);  
                }  
           });  
   
           downloadDialog.show();  
      }  

Android - Intercepting SMS messages and consuming it

Sometimes we have to send messages to our Android apps through SMS messages.
So we have to write a SMS receiver to receive the message. After handling the message, we can decide if we want to allow the SMS message to be displayed in the inbox of the default messaging client or not.

To acheive this, we need to do the following:

1. Add an extra permission to receive the incoming SMS message
2. Declare our SMS receiver in the AndroidManifest xml file
3. Writing a SMS receiver class to handle received messages
4. Decide if we have to showup the message in the default messaging client

Permissions needed
 android.permission.RECEIVE_SMS  

Adding receiver to Android Manifest
Add the sms receiver class to your manifest. If you want your app to handle the SMS message before any other app, then set the priority to the maximum: 999
Setting the maximum priority also comes with added responsibility. You can actually mess up other SMS messages that you dont want to handle from reaching the messaging app.

Add the following lines to AndroidManifest.xml
           <uses-permission android:name="android.permission.RECEIVE_SMS" />  
   
     <receiver android:name=".MySMSReceiver">        
        <intent-filter android:priority="999">   
             <action android:name="android.provider.Telephony.SMS_RECEIVED" />  
           </intent-filter>  
     </receiver>  
   

Receiver code
The MySMSReceiver class should extend the BroadcastReceiver and override the onReceive() method
Assuming the app package is com.test.myapp, add this code to MySMSReceiver.java

 package com.test.myapp;  
   
 import android.content.BroadcastReceiver;  
 import android.content.Context;  
 import android.content.Intent;  
 import android.os.Bundle;  
 import android.telephony.gsm.SmsMessage;  
 import android.widget.Toast;  
   
 public class MySMSReceiver extends BroadcastReceiver {  
   
      @Override  
      public void onReceive(Context context, Intent intent) {  
           String smsData = null;  
   
           if (intent.getAction().equals(android.provider.Telephony.SMS_RECEIVED)) {  
                  
                Bundle pudsBundle = intent.getExtras();  
              Object[] pdus = (Object[]) pudsBundle.get("pdus");  
              SmsMessage messages =SmsMessage.createFromPdu((byte[]) pdus[0]);       
              smsData = messages.getMessageBody();  
                     
                // now smsData has the actual SMS message  
                // Add code to handle the message  
   
           }  
      }  
 }  

Now using the above code, you can intercept the SMS messages.
This will also allow the default messaging client to receive the same SMS message and display it in the Inbox.
What if you want to consume the message and not show it in the messaging app?
Just call abortBroadcast()

 
 public class MySMSReceiver extends BroadcastReceiver {

 @Override
 public void onReceive(Context context, Intent intent) {
  String smsData = null;
  boolean bHandled = false;
  
  if (intent.getAction().equals(android.provider.Telephony.SMS_RECEIVED)) {
   
   Bundle pudsBundle = intent.getExtras();
      Object[] pdus = (Object[]) pudsBundle.get("pdus");
      SmsMessage messages =SmsMessage.createFromPdu((byte[]) pdus[0]);         
      smsData = messages.getMessageBody();
        
   // now smsData has the actual SMS message
   // Add code to handle the message
   // If this message should be aborted set bHandled = true
   
      if (bHandled) {
    abortBroadcast();
   }  
  }
 }
}