Thursday 5 September 2013

Print Receipt Using Bluetooth with Android Phone

Recently, I was asked to make a program for printing some data to a small or handheld Bluetooth printer device. This can be used for printing receipts, simple tickets, or customer notes. I like how it works because usually, we are getting our program output on a computer screen, but  this time we are getting our output on a tangible paper!

I tried everything for printing but my thermal printer was printing only blank paper. At the end I got the issue that is my printer paper is set from the wrong side. Yes that also may be one issue in your case because I went through it . so cheque your paper side if you are getting blank output page.

Now I am going to explain how I  connect to the Bluetooth printer, then make socket connection and then output the string that you put into the edittext.


MainActivity.java - contains button listeners and functions for bluetooth connection, sending data, etc.


 package mmsl.BluetoothFilePrinter;  
 import java.io.BufferedReader;  
 import java.io.FileNotFoundException;  
 import java.io.IOException;  
 import java.io.InputStream;  
 import java.io.InputStreamReader;  
 import java.io.OutputStream;  
 import mmsl.GetPrintableImage.GetPrintableImage;  
 import android.app.Activity;  
 import android.bluetooth.BluetoothSocket;  
 import android.content.ContentValues;  
 import android.content.Intent;  
 import android.graphics.Bitmap;  
 import android.graphics.BitmapFactory;  
 import android.net.Uri;  
 import android.os.Bundle;  
 import android.os.Environment;  
 import android.provider.MediaStore;  
 import android.view.View;  
 import android.view.View.OnClickListener;  
 import android.widget.Button;  
 import android.widget.CheckBox;  
 import android.widget.CompoundButton;  
 import android.widget.CompoundButton.OnCheckedChangeListener;  
 import android.widget.EditText;  
 import android.widget.RadioButton;  
 import android.widget.Toast;  
 public class MainActivity extends Activity {  
  /** Called when the activity is first created. */  
  EditText mNameEditText;  
  EditText mAmountEditText;  
  Button mPrintButton;  
  byte FontStyleVal;  
  private static BluetoothSocket mbtSocket;  
  private static OutputStream mbtOutputStream;  
  private boolean PrintImage = false;  
  int mPrintType = 0;  
  @Override  
  public void onCreate(Bundle savedInstanceState) {  
   super.onCreate(savedInstanceState);  
   setContentView(R.layout.main);  
   mNameEditText = (EditText) findViewById(R.id.nameEditText);  
   mAmountEditText = (EditText) findViewById(R.id.amountEditText);  
   mPrintButton = (Button) findViewById(R.id.printButton);  
   mPrintButton.setOnClickListener(new OnClickListener() {  
    @Override  
    public void onClick(View v) {  
     mPrintType = 0;  
     StartBluetoothConnection();  
    }  
   });  
  }  
  private int[] Readcsv() {  
   try {  
    InputStream in = getAssets().open("logo.txt");// openFileInput();  
    BufferedReader reader = new BufferedReader(  
      new InputStreamReader(in));  
    int[] mona = new int[14926];  
    int cnt = 0;  
    try {  
     String line;  
     while ((line = reader.readLine()) != null) {  
      String[] RowData = line.split(",");  
      for (int i = 0; i < RowData.length; i++) {  
       mona[cnt] = Integer.parseInt(  
         RowData[i].replace("0x", "").replace(" ", ""),  
         16);  
       cnt++;  
      }  
      // do something with "data" and "value"  
     }  
    } catch (Exception ex) {  
     ex.printStackTrace();  
     // handle exception  
    } finally {  
     try {  
      in.close();  
     } catch (IOException e) {  
      // handle exception  
     }  
    }  
    return mona;  
   } catch (Exception e) {  
    return null;  
   }  
  }  
  protected void StartBluetoothConnection() {  
   if (mbtSocket == null) {  
    Intent BTIntent = new Intent(getApplicationContext(),  
      BTWrapperActivity.class);  
    this.startActivityForResult(BTIntent,  
      BTWrapperActivity.REQUEST_CONNECT_BT);  
   } else {  
    // mbtSocket.connect();  
    OutputStream tmpOut = null;  
    try {  
     tmpOut = mbtSocket.getOutputStream();  
    } catch (IOException e) {  
     e.printStackTrace();  
    }  
    mbtOutputStream = tmpOut;  
    senddatatodevice();  
   }  
  }  
  private void senddatatodevice() {  
   try {  
    mbtOutputStream = mbtSocket.getOutputStream();  
    switch (mPrintType) {  
    case 0:  
     byte[] Command = { 0x1B, 0x21, FontStyleVal };  
     mbtOutputStream.write(Command);  
     String sendingmessage = "Name : "  
       + mNameEditText.getText().toString();  
     byte[] send = sendingmessage.getBytes();  
     mbtOutputStream.write(send);  
     mbtOutputStream.write(0x0D);  
     sendingmessage = "Amount : "  
       + mAmountEditText.getText().toString();  
     send = sendingmessage.getBytes();  
     mbtOutputStream.write(send);  
     mbtOutputStream.write(0x0D);  
     mbtOutputStream.write(0x0D);  
     mbtOutputStream.write(0x0D);  
     mbtOutputStream.write(0x0D);  
     mbtOutputStream.flush();  
     break;  
      default:  
     break;  
    }  
    // outstream.write(0);  
    // mbtOutputStream.close();  
    // mbtOutputStream.flush();  
   } catch (IOException e) {  
    e.printStackTrace();  
   }  
  }  
  @Override  
  protected void onDestroy() {  
   super.onDestroy();  
   try {  
    if (mbtSocket != null) {  
     mbtOutputStream.close();  
     mbtSocket.close();  
     mbtSocket = null;  
    }  
   } catch (IOException e) {  
    e.printStackTrace();  
   }  
  }  
  @Override  
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
   super.onActivityResult(requestCode, resultCode, data);  
   switch (requestCode) {  
   case BTWrapperActivity.REQUEST_CONNECT_BT:  
    try {  
     mbtSocket = BTWrapperActivity.getSocket();  
     if (mbtSocket != null) {  
      if (PrintImage == false) {  
       Thread.sleep(100);  
       senddatatodevice();  
      }  
     }  
    } catch (Exception e) {  
     e.printStackTrace();  
    }  
    break;  
   }  
  }  
 }  



BTWrapperActivity:- check the Bluetooth connection and list all the available Bluetooth devices in the rage of you phone. Select the name of the Bluetooth printer and it automatically create one socket connection with that device and send the data byte by byte.


 package com.trust.trust_bank;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.UUID;

import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

// =============== Basically is a state machine, and the communications will happen in a separate thread so as not to obstruct the main UI

public class BTWrapperActivity extends ListActivity{

 static public final int REQUEST_CONNECT_BT = 0x2300;

 static private final int REQUEST_ENABLE_BT = 0x1000;

 static private BluetoothAdapter mBluetoothAdapter = null;

 static private ArrayAdapter<String> mArrayAdapter = null;

 //static private Set<BluetoothDevice> btDevices = null;
 static private ArrayAdapter<BluetoothDevice> btDevices = null;//BluetoothDevice[] btDevices = null; 
 private BluetoothDevice btDevices1=null;
 // Unique UUID for this application, Basically the SPP Profile
 private static final UUID SPP_UUID = 
  // UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
  UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

 static private BluetoothSocket mbtSocket = null;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Change the title of the activity
  setTitle("Bluetooth Devices");

  // Get the List of paired and available devices
  try {
   if(initDevicesList() != 0) {
    this.finish();
    return;
   }

  } catch(Exception ex) {
   this.finish();
   return;
  }

  // Register the Broadcast receiver for handling new BT device discovery
  IntentFilter btIntentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
  registerReceiver(mBTReceiver, btIntentFilter);
 }

 public static BluetoothSocket getSocket() {
  return mbtSocket;
 }

 private void flushData() {
  try {
   if(mbtSocket != null) {
    mbtSocket.close();
    mbtSocket = null;
   }

   if(mBluetoothAdapter != null) {
    mBluetoothAdapter.cancelDiscovery();
   }

   if(btDevices != null) {
    btDevices.clear();
    btDevices = null;
   }

   if(mArrayAdapter != null) {
    mArrayAdapter.clear();    
    mArrayAdapter.notifyDataSetChanged();
    mArrayAdapter.notifyDataSetInvalidated();
    mArrayAdapter = null;
   }

   finalize();

  } catch(Exception ex){} 
  catch (Throwable e) {}

 }


 // Do not forget to add the permission for Bluetooth to use this method
 // Also this method is very tightly coupled with the above method, for getting the status of bt connection 

 private int initDevicesList() {

  // Flush any Pending Data
  flushData();

  // Get the Bluetooth Adaptor of the device
  mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  if(mBluetoothAdapter == null) {
   // Weird Condition ==== Implies that the device does not have a bluetooth module ?????
   Toast.makeText(getApplicationContext(), "Bluetooth not supported!!", Toast.LENGTH_LONG).show();    
   return -1;
  }

  if(mBluetoothAdapter.isDiscovering()) {
   mBluetoothAdapter.cancelDiscovery();
  }

  mArrayAdapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1);

  mArrayAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
  
  
  setListAdapter(mArrayAdapter);


  // get the list of devices already paired
  Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  try {
   startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
  } catch(Exception ex){
   //ex.getStackTrace();
   return -2;
  }


  Toast.makeText(getApplicationContext(), "Getting all available Bluetooth Devices", Toast.LENGTH_SHORT).show();

  return 0;

 } // End getDeviceList

 
 
 @Override
 protected void onActivityResult(int reqCode, int resultCode, Intent intent) {
  super.onActivityResult(reqCode, resultCode, intent);

  switch(reqCode) {
  case REQUEST_ENABLE_BT:

   if(resultCode == RESULT_OK) {
    // Start getting the paired devices list
    Set<BluetoothDevice> btDeviceList = mBluetoothAdapter.getBondedDevices();
    // If there are paired devices
    try {
     if (btDeviceList.size() > 0) {

      // Loop through paired devices
      for (BluetoothDevice device : btDeviceList) {
       if(btDeviceList.contains(device) == false) {

        btDevices.add(device); // Add the device to the device list

        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress() /*+ "\n" + "Status : Paired"*/);
        //mArrayAdapter.notifyDataSetChanged();
        mArrayAdapter.notifyDataSetInvalidated();
       }
      }
     }
    } catch(Exception ex){}
   }


   break;
  }

  // Also register for new devices which are discovered
  mBluetoothAdapter.startDiscovery();

 } // End onActivityResult

 private final BroadcastReceiver mBTReceiver = new BroadcastReceiver() {

  @Override
  public void onReceive(Context context, Intent intent) {
   String action = intent.getAction();
   // When discovery finds a device
   if (BluetoothDevice.ACTION_FOUND.equals(action)) {
    // Get the BluetoothDevice object from the Intent
    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

    try{     

     // No paired device found
     if(btDevices == null )
     {    
      btDevices = new ArrayAdapter<BluetoothDevice>(getApplicationContext(), android.R.id.text1);      
     }

     // Ensure non repetability
     if( btDevices.getPosition(device) < 0 ) {
      btDevices.add(device);     
      // Add the name and address to an array adapter to show in a ListView
      mArrayAdapter.add(device.getName() + "\n" + device.getAddress() +"\n" /*+ "Status : Unpaired"*/);
      //mArrayAdapter.notifyDataSetChanged();
      mArrayAdapter.notifyDataSetInvalidated();
     }
    } catch(Exception ex) 
    {
     //ex.fillInStackTrace();
    }
   }
  }
 };

 @Override
 protected void onListItemClick(ListView l, View v, final int position, long id) {
  super.onListItemClick(l, v, position, id);

  // Dont proceed if the bluetooth adapter is not valid
  if(mBluetoothAdapter == null) {
   return;
  }

  // Cancel the dicovery if still going on
  if(mBluetoothAdapter.isDiscovering()) {   
   mBluetoothAdapter.cancelDiscovery();
  }

  // Try to connect with the selected device,
  Toast.makeText(getApplicationContext(), "Connecting to " + btDevices.getItem(position).getName() + "," + btDevices.getItem(position).getAddress(), Toast.LENGTH_SHORT).show();

  // made the thread different as the connecting proceedure might break down the system
  Thread connectThread = new Thread(new Runnable() {

   @Override
   public void run() {
    try {
     try {
      Method m = btDevices.getItem(position).getClass().getMethod("createInsecureRfcommSocket", 
        new Class[] {int.class});
      try {
       //================================================================
       
       String address=btDevices.getItem(position).toString();
       mBluetoothAdapter.getRemoteDevice(address);
       btDevices1=mBluetoothAdapter.getRemoteDevice(address);
      UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
       mbtSocket = btDevices1.createRfcommSocketToServiceRecord(uuid);
       mbtSocket.connect();
       //=================================================================
       //mbtSocket = (BluetoothSocket) m.invoke(btDevices.getItem(position), 1);
       //m.invoke(receiver, args)
      } catch (IllegalArgumentException e) {
       e.printStackTrace();
      } /*catch (IllegalAccessException e) {
       e.printStackTrace();
      } catch (InvocationTargetException e) {
       e.printStackTrace();
      } */
     } catch (SecurityException e) {
      e.printStackTrace();
     } catch (NoSuchMethodException e) {
      e.printStackTrace();
     } 
    // mbtSocket = btDevices.getItem(position).createRfcommSocketToServiceRecord(SPP_UUID);
     
    // mbtSocket.connect();
    } catch(IOException ex) {
     //Toast.makeText(getApplicationContext(), "Unable to connect to " + btDevices.getItem(position).getName(), Toast.LENGTH_LONG).show();
     runOnUiThread(socketErrorRunnable);
     //Handler myHandler = new Handler();
     //myHandler.post(socketErrorRunnable);
     try {
      mbtSocket.close();
     } catch (IOException e) {
      //e.printStackTrace();
     }
     mbtSocket = null;
     return;
    } finally {
     runOnUiThread(new Runnable() {

      @Override
      public void run() {
       //flushData();
       unregisterReceiver(mBTReceiver);
       finish();

      }
     });
    }
   }
  });

  connectThread.start();
 }

 private Runnable socketErrorRunnable = new Runnable() {

  @Override
  public void run() {
   Toast.makeText(getApplicationContext(), "Cannot establish connection", Toast.LENGTH_SHORT).show();
   mBluetoothAdapter.startDiscovery();

  }
 };

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  super.onCreateOptionsMenu(menu);

  // Add the menu options
  menu.add(0, Menu.FIRST, Menu.NONE, "Refresh Scanning");

  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  super.onOptionsItemSelected(item);

  switch(item.getItemId()) {
  case Menu.FIRST:
   initDevicesList();
   break;
  }

  return true;
 }
} // End of class definition
 



Main.xml:- contains the layout of the app.


 <?xml version="1.0" encoding="utf-8"?>  
 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"  
   android:layout_width="fill_parent"  
   android:layout_height="fill_parent" >  
   <LinearLayout  
     android:layout_width="fill_parent"  
     android:layout_height="fill_parent"  
     android:orientation="vertical" >  
     <TableRow  
       android:id="@+id/TableRow01"  
       android:layout_width="fill_parent"  
       android:layout_height="wrap_content" >  
       <TextView  
         android:id="@+id/nameTextView"  
         android:layout_width="wrap_content"  
         android:layout_height="wrap_content"  
         android:text="Enter Name  : " >  
       </TextView>  
       <EditText  
         android:id="@+id/nameEditText"  
         android:layout_width="fill_parent"  
         android:layout_height="wrap_content"  
         android:singleLine="true"  
         android:text="" >  
       </EditText>  
     </TableRow>  
     <TableRow  
       android:id="@+id/TableRow02"  
       android:layout_width="fill_parent"  
       android:layout_height="wrap_content" >  
       <TextView  
         android:id="@+id/amountTextView"  
         android:layout_width="wrap_content"  
         android:layout_height="wrap_content"  
         android:text="Enter Amount : " >  
       </TextView>  
       <EditText  
         android:id="@+id/amountEditText"  
         android:layout_width="fill_parent"  
         android:layout_height="wrap_content"  
         android:numeric="decimal"  
         android:singleLine="true"  
         android:text="" >  
       </EditText>  
     </TableRow>  
           <Button  
             android:id="@+id/printButton"  
             android:layout_width="fill_parent"  
             android:layout_height="wrap_content"  
             android:text="Print Form" >  
           </Button>  
     </LinearLayout>  
     </ScrollView>  


Note :-Most importantally you have to take the permission of Bluetooth access. Without taking permission you cannot access Bluetooth of your phone. And app will crash automatically.


Add below code into the manifest.xml:-


 <uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>  
 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>  

Hope this code will help someone...

Happy coding....:)

26 comments:

  1. Nice Post.. Its very help full for me to become a good android programmar

    ReplyDelete
  2. Are these constants that you set or are they vendor specific?
    static public final int REQUEST_CONNECT_BT = 0x2300;
    static private final int REQUEST_ENABLE_BT = 0x1000;

    ReplyDelete
  3. good one.. works perfectly.. but the color of list view is white ..I cannot see the list view in some mobile unless i press on particular list item.. i could not find any ways to change the color ..can you tell me how to change the color of list view??

    ReplyDelete
    Replies
    1. Thank you Sushanth. I have updated the code for BTWrapperActivity class, now you can see the list with black background.

      Delete
  4. how to print receipt in different languge like tamil,hindi........etc

    ReplyDelete
    Replies
    1. Hi Murugan you have to use Typeface for it. My previous post help you to do that http://androidnutts.blogspot.in/2012/11/how-to-use-hindi-font-in-android.html

      Delete
  5. Actuallly am using MPD2 model Printer this printer not support for tamil font and only printing chinese laguage ...i tried Typeface also ..but its not working...help me to get tamil font....thank u...

    ReplyDelete
  6. how can i format the text alignment and make it bold ? .. by the way thanks for this post ..

    ReplyDelete
  7. any idea on how to print with image sir? thanks!

    ReplyDelete
  8. can u print other language(other then english) values

    ReplyDelete
  9. InputStream in = getAssets().open("logo.txt");// openFileInput();

    can u upload logo.txt to this post?
    cause when i running , android is error and stop.

    ReplyDelete
  10. Hi, After sending data to the device senddatatodevice(), nothing happens. Printer does not print data. The apps works well, it connects to the printer but does not actually print.

    ReplyDelete
  11. This comment has been removed by the author.

    ReplyDelete
  12. Hi,How to print bill receipt like table format.Please help me i struggle lot

    ReplyDelete
  13. hi. how to print receipt with barcode? thanks.

    ReplyDelete
  14. hi,its really helpful,thank you Manish Burade sir.

    ReplyDelete
  15. can you please guide me what if i have to print listview data somthing like bill. thank you sir

    ReplyDelete
  16. how to print image from canvas encoded data Using Bluetooth with Android Phone

    ReplyDelete
  17. Error:(11, 30) error: package mmsl.GetPrintableImage does not exist

    How solve this..

    ReplyDelete
  18. blutetooth device scanning page doesnt shows any device list

    ReplyDelete