2017-08-22 10:03:42 +00:00
|
|
|
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;
|
2017-08-22 14:58:45 +00:00
|
|
|
import android.bluetooth.BluetoothGattDescriptor;
|
2017-08-22 10:03:42 +00:00
|
|
|
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;
|
2017-08-22 14:35:36 +00:00
|
|
|
import android.os.Handler;
|
2017-08-22 10:03:42 +00:00
|
|
|
import android.os.ParcelUuid;
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
import org.apache.commons.lang3.ArrayUtils;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
// 藍芽狀態
|
|
|
|
public int mState = 0x00;
|
2017-08-22 14:58:45 +00:00
|
|
|
private boolean isConnected = false;
|
2017-08-22 10:03:42 +00:00
|
|
|
|
|
|
|
// 藍芽通訊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;
|
|
|
|
|
2017-08-22 14:35:36 +00:00
|
|
|
private Handler mHandler = new Handler();
|
|
|
|
|
2017-08-22 10:03:42 +00:00
|
|
|
public interface stateChangeListener {
|
|
|
|
void onChange(int state);
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface notifyListener {
|
|
|
|
void onNotify(String msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
2017-08-22 14:58:45 +00:00
|
|
|
if(!isConnected) {
|
|
|
|
isConnected = true;
|
|
|
|
mBtGatt.discoverServices();
|
|
|
|
}
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
2017-08-22 14:35:36 +00:00
|
|
|
mHandler.postDelayed(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
mStateListener.onChange(mState);
|
|
|
|
}
|
|
|
|
}, 100);
|
2017-08-22 10:03:42 +00:00
|
|
|
} 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) {
|
|
|
|
BluetoothGattService btService = mBtGatt.getService(UUID.fromString(serviceUUID));
|
|
|
|
if (btService != null) {
|
|
|
|
printerCharacteristic = btService.getCharacteristic(UUID.fromString(characteristicUUID));
|
|
|
|
// set notify enable
|
|
|
|
mBtGatt.setCharacteristicNotification(printerCharacteristic, true);
|
2017-08-22 14:58:45 +00:00
|
|
|
BluetoothGattDescriptor descriptor = printerCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
|
|
|
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
|
|
|
|
mBtGatt.writeDescriptor(descriptor);
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2017-08-22 14:35:36 +00:00
|
|
|
runQueue();
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@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) 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 = CONNECTING;
|
2017-08-23 14:31:59 +00:00
|
|
|
if(this.mStateListener != null) {
|
|
|
|
this.mStateListener.onChange(mState);
|
|
|
|
}
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-08-23 14:31:59 +00:00
|
|
|
if (mState == CONNECTING) {
|
|
|
|
mState = DISCONNECTED;
|
|
|
|
if(this.mStateListener != null) {
|
|
|
|
this.mStateListener.onChange(mState);
|
|
|
|
}
|
|
|
|
}
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 中斷與設備的連線
|
|
|
|
*/
|
|
|
|
public void disconnect() {
|
|
|
|
mState = DISCONNECTED;
|
2017-08-22 14:58:45 +00:00
|
|
|
isConnected = false;
|
2017-08-22 10:03:42 +00:00
|
|
|
if (mBtGatt != null) mBtGatt.disconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 與指定設備建立連線
|
|
|
|
*
|
|
|
|
* @param device
|
|
|
|
*/
|
|
|
|
public void connect(BluetoothDevice device) {
|
|
|
|
if (device == null) return;
|
|
|
|
mBtGatt = device.connectGatt(ctx, false, mBtGattCB);
|
|
|
|
mState = CONNECTING;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) 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)));
|
|
|
|
}
|
|
|
|
|
2017-08-22 14:35:36 +00:00
|
|
|
public void sendQueue() {
|
2017-08-22 10:03:42 +00:00
|
|
|
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 void textTab() {
|
|
|
|
addText("\t");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void textNewLine() {
|
|
|
|
addText("\n");
|
|
|
|
}
|
|
|
|
|
2017-08-22 14:35:36 +00:00
|
|
|
public PrinterBle addText(String txt) {
|
|
|
|
if (txt == null || txt.isEmpty()) return this;
|
2017-08-22 10:03:42 +00:00
|
|
|
dataQueue.add(txt);
|
2017-08-22 14:35:36 +00:00
|
|
|
return this;
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void addTextln(String txt) {
|
|
|
|
if (txt == null || txt.isEmpty()) return;
|
|
|
|
addText(txt + "\n");
|
|
|
|
}
|
|
|
|
|
2017-08-22 14:35:36 +00:00
|
|
|
public PrinterBle setAlign(int align) {
|
2017-08-22 10:03:42 +00:00
|
|
|
String strAlign = "";
|
|
|
|
switch (align) {
|
|
|
|
case ALIGN_CENTER:
|
|
|
|
strAlign = "ct";
|
|
|
|
break;
|
|
|
|
case ALIGN_LEFT:
|
|
|
|
strAlign = "lt";
|
|
|
|
case ALIGN_RIGHT:
|
|
|
|
strAlign = "rt";
|
|
|
|
default:
|
2017-08-22 14:35:36 +00:00
|
|
|
return this;
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
2017-08-23 14:31:59 +00:00
|
|
|
addText("__&a" + strAlign + "__");
|
2017-08-22 14:35:36 +00:00
|
|
|
return this;
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setSize(int w, int h) {
|
|
|
|
if (w < 1 || h < 1 || w > 3 || h > 3) return;
|
2017-08-23 14:31:59 +00:00
|
|
|
addText(String.format("__&s%d,%d__", w, h));
|
2017-08-22 10:03:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void resetSize() {
|
|
|
|
setSize(1, 1);
|
|
|
|
}
|
|
|
|
}
|