I have made native android modules for react-native. I ejected the app and added the native code to print using a blue-tooth thermal printer. I have tested the native android code using android studio and it printed well. After I have added the module to react-native android, it printed text a few times. Later on, it stopped printing. I always get the imported module as undefined in Javascript.
BlueToothUtils.java: This file is inside react-projectfolder\android\app\src\main\java\\thermal_printer\
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;
public class BlueToothUtils extends ReactContextBaseJavaModule {
public static BluetoothAdapter bluetoothAdapter=null;
public static BluetoothDevice chosen_printer=null;
BluetoothSocket bt_socket = null;
public static UUID DEFAULT_UUID = null;
public UUID device_uuid=null;
public OutputStream output_stream=null;
boolean bluetooth_status=false;
String error_message="";
@Override
public String getName() {
return "BlueToothUtils";
}
public BlueToothUtils(ReactApplicationContext reactContext) {
super(reactContext);
context=reactContext;
}
@ReactMethod
public void initAdapter(Callback returnStatus)
{
try {
DEFAULT_UUID = UUID.fromString("00000000-0000-1000-8000-00805F9B34FB");
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
bluetooth_status = false;
error_message = "Cannot access Bluetooth adapter. Make sure bluetooth is turned ON.";
} else if (bluetoothAdapter.isEnabled())
bluetooth_status = true;
else {
bluetooth_status = false;
error_message = "Bluetooth not enabled.";
}
if(bluetooth_status)
returnStatus.invoke(true,"");
else
returnStatus.invoke(false,error_message);
}catch (Exception ex){returnStatus.invoke(false,ex.getMessage());}
}
@ReactMethod
public void getPairedDevices(Callback returnCallback)
{
try {
if (!bluetooth_status)
returnCallback.invoke(false, "[]");
else {
PairedDevice paired_device;
Set<BluetoothDevice> bonded_devices = bluetoothAdapter.getBondedDevices();
String json = "[";
boolean found = false;
for (BluetoothDevice device : bonded_devices) {
found = true;
paired_device = new PairedDevice(device.getName(), device.getAddress());
json += paired_device.toString() + ",";
}
if (found)
json = json.substring(0, json.length() - 1) + "]";
else
json = "[]";
returnCallback.invoke(true, json);
}
}catch (Exception ex){returnCallback.invoke(false,ex.getMessage());}
}
@ReactMethod
public void chooseDevice(String address,Callback returnCallback)
{
try {
chosen_printer = bluetoothAdapter.getRemoteDevice(address);
returnCallback.invoke(true,"Chosen Successfully");
}catch (Exception ex){returnCallback.invoke(false,ex.getMessage());}
}
public static OutputStream getOutputStream() throws IOException {
BluetoothSocket socket=null;
UUID uuid= chosen_printer.getUuids()[0].getUuid();
try {
socket = chosen_printer.createInsecureRfcommSocketToServiceRecord(uuid);
}catch (Exception ex){
try{
socket=chosen_printer.createInsecureRfcommSocketToServiceRecord(DEFAULT_UUID);
}catch (Exception exc){exc.printStackTrace();Log.d("ttt", exc.toString());}
}
if (socket==null)
{
return null;
}
bluetoothAdapter.cancelDiscovery();
socket.connect();
OutputStream os=socket.getOutputStream();
return os;
}
}
The other module in the same package is PrintUtils.java:
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import java.io.OutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.content.res.AssetManager;
public class PrintUtils extends ReactContextBaseJavaModule {
public static final int ALIGN_LEFT = 0;
public static final int ALIGN_CENTER = 1;
public static final int ALIGN_RIGHT = 2;
public static final int FONT_NORMAL = 0;
public static final int FONT_BOLD_AND_NORMAL_SIZE = 1;
public static final int FONT_BOLD_AND_MEDIUM_SIZE = 2;
public static final int FONT_BOLD_AND_LARGE_SIZE = 3;
public static final byte[] ESC_ALIGN_LEFT = new byte[]{0x1b, 'a', 0x00};
public static final byte[] ESC_ALIGN_RIGHT = new byte[]{0x1b, 'a', 0x02};
public static final byte[] ESC_ALIGN_CENTER = new byte[]{0x1b, 'a', 0x01};
public static final byte LF = 0x0A;
public static byte[] FEED_LINE = {10};
private static String[] binaryArray = {"0000", "0001", "0010", "0011",
"0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011",
"1100", "1101", "1110", "1111"};
private static String hexStr = "0123456789ABCDEF";
public OutputStream outputStream = null;
public boolean is_connected = false;
public ReactApplicationContext context=null;
public PrintUtils(ReactApplicationContext reactContext) {
super(reactContext);
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
public static byte[] sysCopy(List<byte[]> srcArrays) {
int len = 0;
for (byte[] srcArray : srcArrays) {
len += srcArray.length;
}
byte[] destArray = new byte[len];
int destLen = 0;
for (byte[] srcArray : srcArrays) {
System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length);
destLen += srcArray.length;
}
return destArray;
}
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
public static String myBinaryStrToHexString(String binaryStr) {
String hex = "";
String f4 = binaryStr.substring(0, 4);
String b4 = binaryStr.substring(4, 8);
for (int i = 0; i < binaryArray.length; i++) {
if (f4.equals(binaryArray[i]))
hex += hexStr.substring(i, i + 1);
}
for (int i = 0; i < binaryArray.length; i++) {
if (b4.equals(binaryArray[i]))
hex += hexStr.substring(i, i + 1);
}
return hex;
}
public static byte[] hexList2Byte(List<String> list) {
List<byte[]> commandList = new ArrayList<byte[]>();
for (String hexStr : list) {
commandList.add(hexStringToBytes(hexStr));
}
byte[] bytes = sysCopy(commandList);
return bytes;
}
public static List<String> binaryListToHexStringList(List<String> list) {
List<String> hexList = new ArrayList<String>();
for (String binaryStr : list) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < binaryStr.length(); i += 8) {
String str = binaryStr.substring(i, i + 8);
String hexString = myBinaryStrToHexString(str);
sb.append(hexString);
}
hexList.add(sb.toString());
}
return hexList;
}
public static byte[] decodeBitmap(Bitmap bmp) {
int bmpWidth = bmp.getWidth();
int bmpHeight = bmp.getHeight();
List<String> list = new ArrayList<String>(); //binaryString list
StringBuffer sb;
int bitLen = bmpWidth / 8;
int zeroCount = bmpWidth % 8;
String zeroStr = "";
if (zeroCount > 0) {
bitLen = bmpWidth / 8 + 1;
for (int i = 0; i < (8 - zeroCount); i++) {
zeroStr = zeroStr + "0";
}
}
for (int i = 0; i < bmpHeight; i++) {
sb = new StringBuffer();
for (int j = 0; j < bmpWidth; j++) {
int color = bmp.getPixel(j, i);
int r = (color >> 16) & 0xff;
int g = (color >> 8) & 0xff;
int b = color & 0xff;
// if color close to white,bit='0', else bit='1'
if (r > 160 && g > 160 && b > 160)
sb.append("0");
else
sb.append("1");
}
if (zeroCount > 0) {
sb.append(zeroStr);
}
list.add(sb.toString());
}
List<String> bmpHexList = binaryListToHexStringList(list);
String commandHexString = "1D763000";
String widthHexString = Integer
.toHexString(bmpWidth % 8 == 0 ? bmpWidth / 8
: (bmpWidth / 8 + 1));
if (widthHexString.length() > 2) {
Log.e("decodeBitmap error", " width is too large");
return null;
} else if (widthHexString.length() == 1) {
widthHexString = "0" + widthHexString;
}
widthHexString = widthHexString + "00";
String heightHexString = Integer.toHexString(bmpHeight);
if (heightHexString.length() > 2) {
Log.e("decodeBitmap error", " height is too large");
return null;
} else if (heightHexString.length() == 1) {
heightHexString = "0" + heightHexString;
}
heightHexString = heightHexString + "00";
List<String> commandList = new ArrayList<String>();
commandList.add(commandHexString + widthHexString + heightHexString);
commandList.addAll(bmpHexList);
return hexList2Byte(commandList);
}
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("ALIGN_LEFT", ALIGN_LEFT);
constants.put("ALIGN_CENTER", ALIGN_CENTER);
constants.put("ALIGN_RIGHT", ALIGN_RIGHT);
constants.put("FONT_NORMAL", FONT_NORMAL);
constants.put("FONT_BOLD_AND_NORMAL_SIZE", FONT_BOLD_AND_NORMAL_SIZE);
constants.put("FONT_BOLD_AND_MEDIUM_SIZE", FONT_BOLD_AND_MEDIUM_SIZE);
constants.put("FONT_BOLD_AND_LARGE_SIZE", FONT_BOLD_AND_LARGE_SIZE);
constants.put("ESC_ALIGN_LEFT", ESC_ALIGN_LEFT);
constants.put("ESC_ALIGN_CENTER", ESC_ALIGN_CENTER);
constants.put("ESC_ALIGN_RIGHT", ESC_ALIGN_RIGHT);
constants.put("LF", LF);
return constants;
}
@Override
public String getName() {
return "PrintUtils";
}
@ReactMethod
public void printText(String text, int size, int align, Callback returnCallback) {
try {
if (is_connected == false) {
outputStream = BlueToothUtils.getOutputStream();
is_connected = true;
}
//Print config "mode"
byte[] cc = new byte[]{0x1B, 0x21, 0x03}; // 0- normal size text
byte[] bb = new byte[]{0x1B, 0x21, 0x08}; // 1- only bold text
byte[] bb2 = new byte[]{0x1B, 0x21, 0x20}; // 2- bold with medium text
byte[] bb3 = new byte[]{0x1B, 0x21, 0x10}; // 3- bold with large text
switch (size) {
case FONT_NORMAL:
outputStream.write(cc);
break;
case FONT_BOLD_AND_NORMAL_SIZE:
outputStream.write(bb);
break;
case FONT_BOLD_AND_MEDIUM_SIZE:
outputStream.write(bb2);
break;
case FONT_BOLD_AND_LARGE_SIZE:
outputStream.write(bb3);
break;
}
switch (align) {
case ALIGN_LEFT:
//left align
outputStream.write(ESC_ALIGN_LEFT);
break;
case ALIGN_CENTER:
//center align
outputStream.write(ESC_ALIGN_CENTER);
break;
case ALIGN_RIGHT:
//right align
outputStream.write(ESC_ALIGN_RIGHT);
break;
}
outputStream.write(text.getBytes());
outputStream.write(LF);
outputStream.flush();
returnCallback.invoke(true, "success");
} catch (Exception e) {
returnCallback.invoke(false, "error:" + e.getMessage());
}
}
@ReactMethod
public void printPhoto(Callback returnCallback) {
try {
AssetManager assetmgr=getReactApplicationContext().getAssets();
InputStream inp=assetmgr.open("pics/logo.bmp");
Bitmap bmp=BitmapFactory.decodeStream(inp);
if (bmp != null)
{
if (!is_connected)
{
outputStream = BlueToothUtils.getOutputStream();
is_connected = true;
}
byte[] command = PrintUtils.decodeBitmap(bmp);
outputStream.write(new byte[]{0x1b, 'a', 0x01});
outputStream.write(command);
returnCallback.invoke(true, "success");
}
else
{
returnCallback.invoke(false, "error:" + "Image not found");
}
}
catch (Exception e)
{
returnCallback.invoke(false, "error: Exception" + e.getMessage());
}
}
@ReactMethod
public void cutPaper(Callback returnCallback) {
try {
outputStream.write(LF);
outputStream.write(LF);
outputStream.write(LF);
outputStream.flush();
returnCallback.invoke(true, "Success");
} catch (Exception ex) {
returnCallback.invoke(false, "error:" + ex.getMessage());
}
}
@ReactMethod
public void closeConnection(Callback returnCallback) {
try {
outputStream.close();
is_connected = false;
returnCallback.invoke(true, "Success");
} catch (Exception e) {
returnCallback.invoke(false, "error:" + e.getMessage());
}
}
}
There is another class PairedDevice PairedDevice.java in same directory:
public class PairedDevice {
public String name="Noname";
public String address=null;
public PairedDevice(String dev_name, String dev_address) {
name=dev_name;
address=dev_address;
}
public String toString()
{
return "{\"name\":\""+name+"\",\"address\":\""+address+"\"}";
}
}
The package class in same directory is ThermalPrinterPackage :
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.Context;
public class ThermalPrinterPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext)
{
List<NativeModule> modules = new ArrayList<>();
modules.add(new BlueToothUtils(reactContext));
modules.add(new PrintUtils(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
The MainApplication.java file is now as follows:
import dcloud.codesap.com.thermal_printer.*;
import android.support.multidex.MultiDexApplication;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.react.ReactPackage;
import java.util.Arrays;
import java.util.List;
// Needed for `react-native link`
import com.facebook.react.ReactApplication;
public class MainApplication extends MultiDexApplication {
// Needed for `react-native link`
public List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
// Add your own packages here!
// TODO: add cool native modules
// Needed for `react-native link`
new MainReactPackage(),
new ThermalPrinterPackage(),
);
}
}
I am importing and using it as follows:
import BlueToothUtils from '../../BluetoothUtils';
import PrintUtils from '../../PrintUtils';
class Signin extends Component {
constructor(props) {
super(props);
this.state = { username: 'demo',
password: 'demo',
token: '1234'
};
this.getToken = this.getToken.bind(this);
this.navIn = this.navIn.bind(this);
}
async componentDidMount() {
try{
await console.log(BlueToothUtils);
await BlueToothUtils.initAdapter((boo,msg)=>{console.log(boo,msg);});
await BlueToothUtils.getPairedDevices((boo,msg)=>{console.log(boo,'Paired devices:'+msg);});
await BlueToothUtils.chooseDevice("0F:02:17:42:55:C7",(boo,msg)=>{console.log(boo,msg);});
await PrintUtils.printText("Normal Text", PrintUtils.FONT_NORMAL, PrintUtils.ALIGN_LEFT,(boo,msg)=>{console.log(boo,msg);});
await PrintUtils.printText("BOLD Normal text", PrintUtils.FONT_BOLD_AND_NORMAL_SIZE, PrintUtils.ALIGN_CENTER,(boo,msg)=>{console.log(boo,msg);});
await PrintUtils.printText("BOLD Large text", PrintUtils.FONT_BOLD_AND_LARGE_SIZE, PrintUtils.ALIGN_RIGHT,(boo,msg)=>{console.log(boo,msg);});
await PrintUtils.printPhoto((boo,msg)=>{console.log(boo,msg);});
await PrintUtils.cutPaper();
}catch(err){console.log('Error in index.js:componentWillMount: '+err.message)};
}
...
BlueToothUtils.js:
import { NativeModules } from 'react-native';
var bu= NativeModules.BlueToothUtils;
export default bu;
PrintUtils.js:
import { NativeModules } from 'react-native';
var pu=NativeModules.PrintUtils;
export default pu;
It has worked a few times. It did not print image after i added module to react-native. But it did print most of the time. But now it is not printing at all, not even text. Can anyone help? Thanks in advance.
At last I found that the issue was with the
export
of native module. Instead of exporting the instance of the native java object, i exported each method and constant of the object individually. And instead ofexport default
, i usedexport const
The code beccame like this:
I don't know what difference it makes with react-native. But things began to work when i got rid of
export default
. Hope this will help someone.