domingo, 31 de mayo de 2015

Impresión desde Android vía Bluetooth

Hace ya un par de días me fue encargada la actividad de realizar lo que era la impresión de cualquier tipo de texto a una impresora térmica bluetooth , lo cual si lo vemos desde un punto de vista de realización es algo sencillo pero si lo vemos desde el punto de vista de información , es algo realmente difícil , esto ya que no se encuentra información necesaria para realizar esta actividad de manera rápida y sencilla como debería de ser, así que después de una larga investigación en Internet logre tener lo que seria una clase en java la cual permite la realización de esta tarea, claro el ejemplo que pongo a continuación es muy sencillo y ya le deberían de realizar las adecuaciones pertinentes para que realice lo que ustedes requieran hacer.

Lo primero que se realiza es la importación de las librerías necesarias para que trabaje bien la aplicación  y esas son las siguientes.

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;

Después necesitamos declarar lo que seria el formato de la fuente , ya que esta se realiza por medio de secuencias de escape en ASCII , las cuales son en formato PLC y podría decir que es esto es de lo mas difícil de encontrar ya que estas secuencias de escape son únicas para dar el formato a el tipo de letra sin utilizar alguna librería propia de la impresora que usen , en este caso las he probado con lo que es APEX2  y EXTECH , asi que no se como respondan en algunas otras impresoras.

        public static byte[] INIT = { 27, 64 };
public static byte[] RESET = { 24 };
public static final byte[] AUTO_POWER_OFF = { 27, 77, 55, 54, 53, 52, 48,
13 };
public static byte[] SELECT_FONT_A = { 27, 33, 0 };
public static byte[] SELECT_FONT_12 = { 27, 75, 49, 13 };
public static byte[] SELECT_FONT_25 = { 27, 75, 49, 49, 13 };
public static byte[] FONT_BOLD_ON = { 27, 85, 49 };
public static byte[] FONT_BOLD_OFF = { 27, 85, 48 };
public static byte[] FONT_UNDERLINE_ON = { 27, 85, 85 };
public static byte[] FONT_UNDERLINE_OFF = { 27, 85, 117 };
public static byte[] FONT_HI_ON = { 28 };
public static byte[] FONT_HI_OFF = { 29 };
public static byte[] FONT_WIDE_ON = { 14 };
public static byte[] FONT_WIDE_OFF = { 15 };
public static byte[] CHAR_SET = { 27, 70, 49 };
public static byte[] PRINT_LEFT = { 27, 70, 76 };
public static byte[] PRINT_RIGHT = { 27, 70, 86 };
public static byte[] SET_BAR_CODE_HEIGHT = { 29, 104, 100 };
public static byte[] PRINT_BAR_CODE_1 = { 29, 107, 2 };
public static byte[] SEND_NULL_BYTE = { 0x00 };
public static byte[] SELECT_PRINT_SHEET = { 0x1B, 0x63, 0x30, 0x02 };
public static byte[] FEED_PAPER_AND_CUT = { 0x1D, 0x56, 66, 0x00 };
public static byte[] SELECT_CYRILLIC_CHARACTER_CODE_TABLE = { 0x1B, 0x74, 0x11 };

Ahora esta aquí parte de lo que es importante lo que es la identificación de el equipo y la conexión , las cuales están divididas en dos funciones .

primero lo que es la identificación del equipo.

void findBT(Context context) {
try {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

if (mBluetoothAdapter == null) {
Toast.makeText(context, "No bluetooth adapter available",
Toast.LENGTH_LONG).show();
}

if (!mBluetoothAdapter.isEnabled()) {
Intent enableBluetooth = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBluetooth, 0);
}

Set pairedDevices = mBluetoothAdapter
.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
String PrinterName = "D-O Printer";
if (device.getName().equals(PrinterName)) {
mmDevice = device;
break;
}
}
}
Toast.makeText(context, "Bluetooth Device Found", Toast.LENGTH_LONG)
.show();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

Después de haber identificado lo que es el equipo donde vamos a imprimir es necesario abrir una conexión.

void openBT(Context context) throws IOException {
try {
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();

beginListenForData();

Toast.makeText(context, "Bluetooth Opened", Toast.LENGTH_LONG)
.show();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

pero para esto también es necesario tener un listener para poder estar mandando la información .

void beginListenForData() {
try {
final Handler handler = new Handler();
final byte delimiter = 10;

stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];

workerThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted()
&& !stopWorker) {

try {

int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0,
encodedBytes, 0,
encodedBytes.length);
final String data = new String(
encodedBytes, "US-ASCII");
readBufferPosition = 0;

handler.post(new Runnable() {
public void run() {
Log.i("Runnable", data);
}
});
} else {
readBuffer[readBufferPosition++] = b;
}
}
}

} catch (IOException ex) {
stopWorker = true;
}

}
}
});

workerThread.start();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}


Ahora ya podemos mandar la información que necesitamos a la impresora , por medio de esta función.

void ImprimeMensaje(Context context) throws IOException {
try {
mmOutputStream.write(RESET);
mmOutputStream.write(INIT);
mmOutputStream.flush();
mmOutputStream.write(SELECT_FONT_12);
mmOutputStream.write(FONT_BOLD_ON);

mmOutputStream.write(FONT_HI_ON);
String msg = "\n   Hola Mundo \n";
mmOutputStream.write(RESET);
mmOutputStream.write(FONT_BOLD_OFF);

mmOutputStream.write(FONT_HI_OFF);
mmOutputStream.write(msg.getBytes());
mmOutputStream.flush();

Toast.makeText(context, "Data Sent", Toast.LENGTH_LONG).show();

} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

Lo ultimo que se necesita es cerrar la conexión con nuestra impresora y para eso utilizaremos lo que es la siguiente función .

void closeBT(Context context) throws IOException {
try {
stopWorker = true;
mmOutputStream.close();
mmInputStream.close();
mmSocket.close();
Toast.makeText(context, "Bluetooth Closed", Toast.LENGTH_LONG)
.show();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

y pues para darle forma a todo esto yo hice una función que llama a todas estas en orden para realizar la impresión de un "Hola Mundo"

public void imprime(Context context) {
try {

try {
findBT(context);
openBT(context);
} catch (IOException ex) {
}

try {
ImprimeMensaje(context);
} catch (IOException ex) {
}

try {
closeBT(context);
} catch (IOException ex) {
}

} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

Ahora  si analizamos el código nos faltan variables declaradas para poder trabajar mas que nada el tipo de dato para poder hacer las conexiones.

        BluetoothAdapter mBluetoothAdapter;
BluetoothSocket mmSocket;
BluetoothDevice mmDevice;

OutputStream mmOutputStream;
InputStream mmInputStream;
Thread workerThread;

byte[] readBuffer;
int readBufferPosition;
int counter;
volatile boolean stopWorker;

Ahora para todos aquellos que son nuevos programando y ven que tengo una variable tipo context en las funciones es porque como les dije en un inicio es una clase la cual no puede mandar a la actividad que la manda a llamar los Toast a menos de que se les mande el conext en el cual fue llamado. esto lo realizamos de la siguiente forma .

      Printer PT = new Printer();
      PT.imprime(getBaseConext());

Bueno despues de varios meses sin publicar nada bueno aqui les dejo algo que creo puede llegar a ser de mucha utilidad.

Si quieren que suba lo que es la apk imprimiendo solo me dicen y se las publico . Saludos!