├── BluetoothPrinter.java └── README.md /BluetoothPrinter.java: -------------------------------------------------------------------------------- 1 | package cz.martinforejt.bluetooth_printer; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.bluetooth.BluetoothSocket; 5 | import android.graphics.Bitmap; 6 | import android.os.AsyncTask; 7 | 8 | import java.io.IOException; 9 | import java.io.OutputStream; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.UUID; 13 | 14 | /** 15 | * Created by Martin Forejt on 28.06.2017. 16 | * forejt.martin97@gmail.com 17 | */ 18 | 19 | public class BluetoothPrinter { 20 | 21 | public static final int ALIGN_CENTER = 100; 22 | public static final int ALIGN_RIGHT = 101; 23 | public static final int ALIGN_LEFT = 102; 24 | 25 | private static final byte[] NEW_LINE = {10}; 26 | private static final byte[] ESC_ALIGN_CENTER = new byte[]{0x1b, 'a', 0x01}; 27 | private static final byte[] ESC_ALIGN_RIGHT = new byte[]{0x1b, 'a', 0x02}; 28 | private static final byte[] ESC_ALIGN_LEFT = new byte[]{0x1b, 'a', 0x00}; 29 | 30 | private BluetoothDevice printer; 31 | private BluetoothSocket btSocket = null; 32 | private OutputStream btOutputStream = null; 33 | 34 | public BluetoothPrinter(BluetoothDevice printer) { 35 | this.printer = printer; 36 | } 37 | 38 | public void connectPrinter(final PrinterConnectListener listener) { 39 | new ConnectTask(new ConnectTask.BtConnectListener() { 40 | @Override 41 | public void onConnected(BluetoothSocket socket) { 42 | btSocket = socket; 43 | try { 44 | btOutputStream = socket.getOutputStream(); 45 | listener.onConnected(); 46 | } catch (IOException e) { 47 | listener.onFailed(); 48 | } 49 | } 50 | 51 | @Override 52 | public void onFailed() { 53 | listener.onFailed(); 54 | } 55 | }).execute(printer); 56 | } 57 | 58 | public boolean isConnected() { 59 | return btSocket != null && btSocket.isConnected(); 60 | } 61 | 62 | public void finish() { 63 | if (btSocket != null) { 64 | try { 65 | btOutputStream.close(); 66 | btSocket.close(); 67 | } catch (IOException e) { 68 | e.printStackTrace(); 69 | } 70 | btSocket = null; 71 | } 72 | } 73 | 74 | public boolean printText(String text) { 75 | try { 76 | btOutputStream.write(encodeNonAscii(text).getBytes()); 77 | return true; 78 | } catch (IOException e) { 79 | e.printStackTrace(); 80 | return false; 81 | } 82 | } 83 | 84 | public boolean printUnicode(byte[] data) { 85 | try { 86 | btOutputStream.write(data); 87 | return true; 88 | } catch (IOException e) { 89 | e.printStackTrace(); 90 | return false; 91 | } 92 | } 93 | 94 | public boolean printLine() { 95 | return printText("________________________________"); 96 | } 97 | 98 | public boolean addNewLine() { 99 | return printUnicode(NEW_LINE); 100 | } 101 | 102 | public int addNewLines(int count) { 103 | int success = 0; 104 | for (int i = 0; i < count; i++) { 105 | if (addNewLine()) success++; 106 | } 107 | 108 | return success; 109 | } 110 | 111 | public boolean printImage(Bitmap bitmap) { 112 | byte[] command = decodeBitmap(bitmap); 113 | return printUnicode(command); 114 | } 115 | 116 | public void setAlign(int alignType) { 117 | byte[] d; 118 | switch (alignType) { 119 | case ALIGN_CENTER: 120 | d = ESC_ALIGN_CENTER; 121 | break; 122 | case ALIGN_LEFT: 123 | d = ESC_ALIGN_LEFT; 124 | break; 125 | case ALIGN_RIGHT: 126 | d = ESC_ALIGN_RIGHT; 127 | break; 128 | default: 129 | d = ESC_ALIGN_LEFT; 130 | break; 131 | } 132 | 133 | try { 134 | btOutputStream.write(d); 135 | } catch (IOException e) { 136 | e.printStackTrace(); 137 | } 138 | } 139 | 140 | public void setLineSpacing(int lineSpacing) { 141 | byte[] cmd = new byte[]{0x1B, 0x33, (byte) lineSpacing}; 142 | printUnicode(cmd); 143 | } 144 | 145 | public void setBold(boolean bold) { 146 | byte[] cmd = new byte[]{0x1B, 0x45, bold ? (byte) 1 : 0}; 147 | printUnicode(cmd); 148 | } 149 | 150 | public void feedPaper() { 151 | addNewLine(); 152 | addNewLine(); 153 | addNewLine(); 154 | addNewLine(); 155 | } 156 | 157 | private static class ConnectTask extends AsyncTask { 158 | private BtConnectListener listener; 159 | 160 | private ConnectTask(BtConnectListener listener) { 161 | this.listener = listener; 162 | } 163 | 164 | @Override 165 | protected BluetoothSocket doInBackground(BluetoothDevice... bluetoothDevices) { 166 | BluetoothDevice device = bluetoothDevices[0]; 167 | UUID uuid = device.getUuids()[0].getUuid(); 168 | BluetoothSocket socket = null; 169 | boolean connected = true; 170 | 171 | try { 172 | socket = device.createRfcommSocketToServiceRecord(uuid); 173 | } catch (IOException e) { 174 | } 175 | try { 176 | socket.connect(); 177 | } catch (IOException e) { 178 | try { 179 | socket = (BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[]{int.class}) 180 | .invoke(device, 1); 181 | socket.connect(); 182 | } catch (Exception e2) { 183 | connected = false; 184 | } 185 | } 186 | 187 | return connected ? socket : null; 188 | } 189 | 190 | @Override 191 | protected void onPostExecute(BluetoothSocket bluetoothSocket) { 192 | if (listener != null) { 193 | if (bluetoothSocket != null) listener.onConnected(bluetoothSocket); 194 | else listener.onFailed(); 195 | } 196 | } 197 | 198 | private interface BtConnectListener { 199 | void onConnected(BluetoothSocket socket); 200 | 201 | void onFailed(); 202 | } 203 | } 204 | 205 | public interface PrinterConnectListener { 206 | void onConnected(); 207 | 208 | void onFailed(); 209 | } 210 | 211 | private static String encodeNonAscii(String text) { 212 | return text.replace('á', 'a') 213 | .replace('č', 'c') 214 | .replace('ď', 'd') 215 | .replace('é', 'e') 216 | .replace('ě', 'e') 217 | .replace('í', 'i') 218 | .replace('ň', 'n') 219 | .replace('ó', 'o') 220 | .replace('ř', 'r') 221 | .replace('š', 's') 222 | .replace('ť', 't') 223 | .replace('ú', 'u') 224 | .replace('ů', 'u') 225 | .replace('ý', 'y') 226 | .replace('ž', 'z') 227 | .replace('Á', 'A') 228 | .replace('Č', 'C') 229 | .replace('Ď', 'D') 230 | .replace('É', 'E') 231 | .replace('Ě', 'E') 232 | .replace('Í', 'I') 233 | .replace('Ň', 'N') 234 | .replace('Ó', 'O') 235 | .replace('Ř', 'R') 236 | .replace('Š', 'S') 237 | .replace('Ť', 'T') 238 | .replace('Ú', 'U') 239 | .replace('Ů', 'U') 240 | .replace('Ý', 'Y') 241 | .replace('Ž', 'Z'); 242 | } 243 | 244 | public static byte[] decodeBitmap(Bitmap bmp) { 245 | int bmpWidth = bmp.getWidth(); 246 | int bmpHeight = bmp.getHeight(); 247 | 248 | List list = new ArrayList<>(); 249 | StringBuffer sb; 250 | int zeroCount = bmpWidth % 8; 251 | String zeroStr = ""; 252 | if (zeroCount > 0) { 253 | for (int i = 0; i < (8 - zeroCount); i++) zeroStr = zeroStr + "0"; 254 | } 255 | 256 | for (int i = 0; i < bmpHeight; i++) { 257 | sb = new StringBuffer(); 258 | for (int j = 0; j < bmpWidth; j++) { 259 | int color = bmp.getPixel(j, i); 260 | int r = (color >> 16) & 0xff; 261 | int g = (color >> 8) & 0xff; 262 | int b = color & 0xff; 263 | if (r > 160 && g > 160 && b > 160) sb.append("0"); 264 | else sb.append("1"); 265 | } 266 | if (zeroCount > 0) sb.append(zeroStr); 267 | list.add(sb.toString()); 268 | } 269 | 270 | List bmpHexList = binaryListToHexStringList(list); 271 | String commandHexString = "1D763000"; 272 | String widthHexString = Integer 273 | .toHexString(bmpWidth % 8 == 0 ? bmpWidth / 8 : (bmpWidth / 8 + 1)); 274 | if (widthHexString.length() > 2) { 275 | return null; 276 | } else if (widthHexString.length() == 1) { 277 | widthHexString = "0" + widthHexString; 278 | } 279 | widthHexString = widthHexString + "00"; 280 | 281 | String heightHexString = Integer.toHexString(bmpHeight); 282 | if (heightHexString.length() > 2) { 283 | return null; 284 | } else if (heightHexString.length() == 1) { 285 | heightHexString = "0" + heightHexString; 286 | } 287 | heightHexString = heightHexString + "00"; 288 | 289 | List commandList = new ArrayList<>(); 290 | commandList.add(commandHexString + widthHexString + heightHexString); 291 | commandList.addAll(bmpHexList); 292 | 293 | return hexList2Byte(commandList); 294 | } 295 | 296 | private static List binaryListToHexStringList(List list) { 297 | List hexList = new ArrayList<>(); 298 | for (String binaryStr : list) { 299 | StringBuilder sb = new StringBuilder(); 300 | for (int i = 0; i < binaryStr.length(); i += 8) { 301 | String str = binaryStr.substring(i, i + 8); 302 | String hexString = strToHexString(str); 303 | sb.append(hexString); 304 | } 305 | hexList.add(sb.toString()); 306 | } 307 | return hexList; 308 | } 309 | 310 | private static String hexStr = "0123456789ABCDEF"; 311 | private static String[] binaryArray = {"0000", "0001", "0010", "0011", 312 | "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", 313 | "1100", "1101", "1110", "1111"}; 314 | 315 | private static String strToHexString(String binaryStr) { 316 | String hex = ""; 317 | String f4 = binaryStr.substring(0, 4); 318 | String b4 = binaryStr.substring(4, 8); 319 | for (int i = 0; i < binaryArray.length; i++) { 320 | if (f4.equals(binaryArray[i])) 321 | hex += hexStr.substring(i, i + 1); 322 | } 323 | for (int i = 0; i < binaryArray.length; i++) { 324 | if (b4.equals(binaryArray[i])) 325 | hex += hexStr.substring(i, i + 1); 326 | } 327 | 328 | return hex; 329 | } 330 | 331 | private static byte[] hexList2Byte(List list) { 332 | List commandList = new ArrayList<>(); 333 | for (String hexStr : list) commandList.add(hexStringToBytes(hexStr)); 334 | return sysCopy(commandList); 335 | } 336 | 337 | private static byte[] hexStringToBytes(String hexString) { 338 | if (hexString == null || hexString.equals("")) return null; 339 | hexString = hexString.toUpperCase(); 340 | int length = hexString.length() / 2; 341 | char[] hexChars = hexString.toCharArray(); 342 | byte[] d = new byte[length]; 343 | for (int i = 0; i < length; i++) { 344 | int pos = i * 2; 345 | d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); 346 | } 347 | return d; 348 | } 349 | 350 | private static byte[] sysCopy(List srcArrays) { 351 | int len = 0; 352 | for (byte[] srcArray : srcArrays) { 353 | len += srcArray.length; 354 | } 355 | byte[] destArray = new byte[len]; 356 | int destLen = 0; 357 | for (byte[] srcArray : srcArrays) { 358 | System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length); 359 | destLen += srcArray.length; 360 | } 361 | 362 | return destArray; 363 | } 364 | 365 | private static byte charToByte(char c) { 366 | return (byte) "0123456789ABCDEF".indexOf(c); 367 | } 368 | 369 | public BluetoothSocket getSocket() { 370 | return btSocket; 371 | } 372 | 373 | public BluetoothDevice getDevice() { 374 | return printer; 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Bluetooth Printer 2 | Android library for using thermal printers via bluetooth connection. 3 | 4 | ## Example usage 5 | ``` 6 | BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); 7 | BluetoothDevice mBtDevice = btAdapter.getBondedDevices().iterator().next(); // Get first paired device 8 | 9 | final BluetoothPrinter mPrinter = new BluetoothPrinter(mBtDevice); 10 | mPrinter.connectPrinter(new BluetoothPrinter.PrinterConnectListener() { 11 | 12 | @Override 13 | public void onConnected() { 14 | mPrinter.setAlign(BluetoothPrinter.ALIGN_CENTER); 15 | mPrinter.printText("Hello World!"); 16 | mPrinter.addNewLine(); 17 | 18 | mPrinter.finish(); 19 | } 20 | 21 | @Overide 22 | public void onFailed() { 23 | Log.d("BluetoothPrinter", "Conection failed"); 24 | } 25 | 26 | }); 27 | 28 | ``` 29 | Thanks to [imrankst1221](https://github.com/imrankst1221/Thermal-Printer-in-Android) 30 | --------------------------------------------------------------------------------