ble-android-demo/app/src/main/java/xyz/mtfos/btdemo/PrinterBle.java

474 lines
15 KiB
Java

package xyz.mtfos.btdemo;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
import android.util.Log;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
* Created by jay on 2017/8/22.
*/
public class PrinterBle {
final private String TAG = "Bluetooth LE Object";
private Context ctx;
private BluetoothAdapter mAdapter;
private BluetoothGatt mBtGatt;
private BluetoothGattCharacteristic printerCharacteristic;
// for android api >= 21 use
private BluetoothLeScanner mLeScanner;
private ScanSettings mLeSettings;
private List<ScanFilter> mLeFilters;
// BLE相關參數
private String macAddr, serviceUUID, characteristicUUID;
// 藍芽狀態列表
final public static int CONNECTING = 0x01;
final public static int CONNECTED = 0x02;
final public static int DISCONNECTED = 0x00;
final public static int SCANNING = 0x03;
// printer 相關參數
final public static int ALIGN_LEFT = 0x00;
final public static int ALIGN_CENTER = 0x01;
final public static int ALIGN_RIGHT = 0x02;
private int lastAlign = 0x00;
private int lastWidth = 1;
private int lastHeigh = 1;
// 藍芽狀態
public int mState = 0x00;
private boolean isConnected = false;
// 藍芽通訊flag
private boolean sending = false;
// 發送內容陣列
private ArrayList<byte[]> sendingQueue = new ArrayList<>();
// 待處理發送陣列
private ArrayList<String> dataQueue = new ArrayList<>();
// Listener Object
private stateChangeListener mStateListener;
private notifyListener mNotifyListener;
private Handler mHandler = new Handler();
public interface stateChangeListener {
void onChange(int state);
}
public interface notifyListener {
void onNotify(String msg);
}
public PrinterBle(Context ctx) {
this(ctx, "", "", "");
}
public PrinterBle(Context ctx, String macAddr, String serviceUUID, String characteristicUUID) {
this.ctx = ctx;
this.macAddr = macAddr;
this.serviceUUID = serviceUUID;
this.characteristicUUID = characteristicUUID;
}
private BluetoothGattCallback mBtGattCB = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.d(TAG, "Connect State Change to " + status + " new State : " + newState);
if (status == BluetoothGatt.GATT_SUCCESS) {
if (mState != DISCONNECTED) {
mState = CONNECTED;
Log.d(TAG, "Connected");
if (!isConnected) {
isConnected = true;
mBtGatt.discoverServices();
}
}
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mStateListener.onChange(mState);
}
}, 100);
} else if (newState == BluetoothGatt.GATT_FAILURE) {
mState = DISCONNECTED;
Log.d(TAG, "Connect Failed");
mStateListener.onChange(mState);
}
}
// 遠端設備中的服務可用時的回調
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS && !serviceUUID.isEmpty()) {
BluetoothGattService btService = mBtGatt.getService(UUID.fromString(serviceUUID));
if (btService != null && !characteristicUUID.isEmpty()) {
printerCharacteristic = btService.getCharacteristic(UUID.fromString(characteristicUUID));
// set notify enable
mBtGatt.setCharacteristicNotification(printerCharacteristic, true);
BluetoothGattDescriptor descriptor = printerCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBtGatt.writeDescriptor(descriptor);
}
}
}
// write characteristic callback
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
switch (status) {
case BluetoothGatt.GATT_SUCCESS:
Log.d(TAG, "Write Data Success");
break;
case BluetoothGatt.GATT_FAILURE:
Log.d(TAG, "Write Data Filed");
break;
case BluetoothGatt.GATT_WRITE_NOT_PERMITTED:
Log.d(TAG, "Write not permitted");
break;
}
sending = false;
runQueue();
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
readCharacteristicValue(characteristic);
}
};
/**
* 讀取 BLE characteristic 資料
*
* @param characteristic
*/
private void readCharacteristicValue(BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
StringBuffer buffer = new StringBuffer("0x");
int i;
for (byte b : data) {
i = b & 0xff;
buffer.append(Integer.toHexString(i));
}
Log.d(TAG, "ReadData: " + buffer.toString());
Log.d(TAG, "ReadData(As String): " + new String(data));
mNotifyListener.onNotify(new String(data));
}
/**
* 初始化檢查
*
* @return 檢查成功返回true
*/
public boolean init() {
BluetoothManager btMng = (BluetoothManager) ctx.getSystemService(Context.BLUETOOTH_SERVICE);
if (btMng == null) return false;
mAdapter = btMng.getAdapter();
if (mAdapter == null) return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mLeScanner = mAdapter.getBluetoothLeScanner();
if (mLeScanner == null) return false;
mLeSettings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
mLeFilters = new ArrayList<>();
// mLeFilters.add(new ScanFilter.Builder().setServiceUuid(new ParcelUuid(UUID.fromString(serviceUUID))).build());
}
return true;
}
private BluetoothAdapter.LeScanCallback mLeScanCB = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] bytes) {
String devAddr = bluetoothDevice.getAddress();
if (devAddr.equals(macAddr)) {
Log.d(TAG, bluetoothDevice.getName() + " has available service UUID");
// 處理連線配段相關
stopScan();
connect(bluetoothDevice);
}
}
};
private ScanCallback mLeScanCB_21 = new ScanCallback() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onScanResult(int callbackType, ScanResult result) {
// super.onScanResult(callbackType, result);
BluetoothDevice device = result.getDevice();
Log.d(TAG, "Device : " + device.getName() + " Found\nDeviceMac : " + device.getAddress());
if (device.getAddress().equals(macAddr)) {
// 處理連線配段相關
stopScan();
connect(device);
}
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.d(TAG, "BLE Scan Fail Code : " + errorCode);
}
};
public void startScan() {
if (mAdapter == null || macAddr.isEmpty() || serviceUUID.isEmpty() || characteristicUUID.isEmpty()) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mLeScanner == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Log.d(TAG, "Start Scanning");
mLeScanner.startScan(mLeFilters, mLeSettings, mLeScanCB_21);
} else {
mAdapter.startLeScan(mLeScanCB);
}
mState = SCANNING;
if (this.mStateListener != null) {
this.mStateListener.onChange(mState);
}
}
public void stopScan() {
if (mAdapter == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mLeScanner == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mLeScanner.stopScan(mLeScanCB_21);
} else {
mAdapter.stopLeScan(mLeScanCB);
}
if (mState == SCANNING) {
mState = DISCONNECTED;
if (this.mStateListener != null) {
this.mStateListener.onChange(mState);
}
}
}
/**
* 中斷與設備的連線
*/
public void disconnect() {
mState = DISCONNECTED;
isConnected = false;
if (mBtGatt != null) mBtGatt.disconnect();
}
/**
* 與指定設備建立連線
*
* @param device
*/
public void connect(BluetoothDevice device) {
if (device == null) return;
mBtGatt = device.connectGatt(ctx, false, mBtGattCB);
mState = CONNECTING;
if (this.mStateListener != null) {
this.mStateListener.onChange(mState);
}
}
public void setOnStateChangeListener(stateChangeListener listener) {
this.mStateListener = listener;
}
public void setOnNotifyListener(notifyListener listener) {
this.mNotifyListener = listener;
}
private void sendData(byte[] data) {
if (data == null || data.length < 1 || data.length > 20 || printerCharacteristic == null) return;
boolean setVal = printerCharacteristic.setValue(data);
boolean sendVal = mBtGatt.writeCharacteristic(printerCharacteristic);
Log.d(TAG, String.format("setVal: %s / sendVal: %s", String.valueOf(setVal), String.valueOf(sendVal)));
}
public void sendQueue() {
if (sendingQueue == null) sendingQueue = new ArrayList<>();
if (dataQueue == null) dataQueue = new ArrayList<>();
if (dataQueue.size() == 0) return;
sendingQueue.clear();
StringBuilder strbuild = new StringBuilder();
for (String s : dataQueue) {
strbuild.append(s);
}
String str = strbuild.toString();
byte[] dataByte = str.getBytes();
pushQueue(new byte[]{0x00});
int idx = 0;
while (true) {
int st = idx * 18;
int ed = idx * 18 + 18;
ed = ed > dataByte.length ? dataByte.length : ed;
byte[] tmp = Arrays.copyOfRange(dataByte, st, ed);
byte[] sdata = new byte[2 + tmp.length];
sdata[1] = (byte) (idx & 0xff);
sdata[0] = (byte) ((idx >> 8) & 0xff);
for (int i = 2; i < sdata.length; i++) {
if (i - 2 >= tmp.length) break;
sdata[i] = tmp[i - 2];
}
pushQueue(sdata);
idx++;
if (ed >= dataByte.length) break;
}
pushQueue(new byte[]{(byte) 0xff});
dataQueue.clear();
}
private void pushQueue(byte[] data) {
if (data == null || data.length == 0) return;
if (sendingQueue == null) sendingQueue = new ArrayList<>();
sendingQueue.add(data);
runQueue();
}
private void runQueue() {
if (sendingQueue == null) sendingQueue = new ArrayList<>();
if (sendingQueue.size() < 1 || sending == true) return;
sending = true;
byte[] d = sendingQueue.get(0);
sendingQueue.remove(0);
sendData(d);
}
public void clearQueue() {
dataQueue.clear();
}
public PrinterBle textTab() {
return addText("\t");
}
public PrinterBle textNewLine() {
return addText("\n");
}
public PrinterBle addText(String txt) {
if (txt == null || txt.isEmpty()) return this;
dataQueue.add(txt);
return this;
}
public PrinterBle addTextln(String txt) {
if (txt == null || txt.isEmpty()) return this;
return addText(txt + "\n");
}
public PrinterBle setAlign(int align) {
String strAlign = "";
switch (align) {
case ALIGN_CENTER:
strAlign = "ct";
break;
case ALIGN_LEFT:
strAlign = "lt";
break;
case ALIGN_RIGHT:
strAlign = "rt";
break;
default:
return this;
}
this.lastAlign = align;
return addText("__&a" + strAlign + "__");
}
public PrinterBle setSize(int w, int h) {
if (w < 1 || h < 1 || w > 3 || h > 3) return this;
lastWidth = w;
lastHeigh = h;
return addText(String.format("__&s%d,%d__", w, h));
}
public PrinterBle addHR() {
int align = this.lastAlign;
int w = lastWidth;
int h = lastHeigh;
setAlign(ALIGN_CENTER);
resetSize();
textNewLine();
addTextln(StringUtils.repeat('-', 20));
setAlign(align);
setSize(w, h);
return this;
}
public PrinterBle resetSize() {
lastHeigh = 1;
lastWidth = 1;
setSize(1, 1);
return this;
}
public String getMacAddr() {
return macAddr;
}
public void setMacAddr(String macAddr) {
this.macAddr = macAddr;
}
public String getServiceUUID() {
return serviceUUID;
}
public void setServiceUUID(String serviceUUID) {
this.serviceUUID = serviceUUID;
}
public String getCharacteristicUUID() {
return characteristicUUID;
}
public void setCharacteristicUUID(String characteristicUUID) {
this.characteristicUUID = characteristicUUID;
}
public ArrayList<String> getDataQueue() {
return dataQueue;
}
}