package com.shlb.comb.manager;
|
|
import android.bluetooth.BluetoothGatt;
|
import android.bluetooth.BluetoothGattCallback;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattDescriptor;
|
import android.bluetooth.BluetoothGattService;
|
import android.bluetooth.BluetoothProfile;
|
import android.content.Context;
|
import android.os.Handler;
|
import android.os.Looper;
|
import android.widget.Toast;
|
|
import com.blakequ.bluetooth_manager_lib.connect.BluetoothConnectManager;
|
import com.blakequ.bluetooth_manager_lib.connect.ConnectState;
|
import com.blakequ.bluetooth_manager_lib.connect.ConnectStateListener;
|
import com.blakequ.bluetooth_manager_lib.device.BluetoothLeDevice;
|
import com.shlb.comb.event.UpdateEvent;
|
|
import org.greenrobot.eventbus.EventBus;
|
|
import java.util.List;
|
import java.util.UUID;
|
|
public class BleGlobalManager {
|
private static volatile BleGlobalManager instance;
|
private BluetoothConnectManager connectManager;
|
private BluetoothLeDevice mDevice;
|
private BluetoothGatt mGatt;
|
|
private BluetoothGattCharacteristic writeCharacteristic;
|
private BluetoothGattCharacteristic notifyCharacteristic;
|
|
private Context mContext;
|
private Handler mHandler = new Handler(Looper.getMainLooper());
|
|
private BleGlobalManager() {
|
}
|
|
public static BleGlobalManager getInstance() {
|
if (instance == null) {
|
synchronized (BleGlobalManager.class) {
|
if (instance == null) {
|
instance = new BleGlobalManager();
|
}
|
}
|
}
|
return instance;
|
}
|
|
public BluetoothLeDevice getBluetoothLeDevice() {
|
return mDevice;
|
}
|
|
public void connect(Context context, BluetoothLeDevice device) {
|
this.mContext = context;
|
if (connectManager == null) {
|
connectManager = BluetoothConnectManager.getInstance(mContext);
|
connectManager.addConnectStateListener(stateListener);
|
}
|
// 每次连接都重新设置GattCallback,防止被其他Activity(如TestActivity)覆盖
|
connectManager.setBluetoothGattCallback(gattCallback);
|
|
this.mDevice = device;
|
connectManager.connect(device.getAddress());
|
}
|
|
public void disconnect() {
|
if (connectManager != null && mDevice != null) {
|
connectManager.disconnect(mDevice.getAddress());
|
}
|
resetCharacteristics();
|
}
|
|
public boolean isConnected() {
|
return connectManager != null && connectManager.isConnectDevice();
|
}
|
|
private void resetCharacteristics() {
|
writeCharacteristic = null;
|
notifyCharacteristic = null;
|
mGatt = null;
|
}
|
|
private ConnectStateListener stateListener = new ConnectStateListener() {
|
@Override
|
public void onConnectStateChanged(String address, ConnectState state) {
|
if (state == ConnectState.CONNECTED) {
|
EventBus.getDefault().post(new UpdateEvent(UpdateEvent.Type.CONN_STATU, BluetoothProfile.STATE_CONNECTED, "conn_statu"));
|
} else if (state == ConnectState.NORMAL) {
|
EventBus.getDefault().post(new UpdateEvent(UpdateEvent.Type.CONN_STATU, BluetoothProfile.STATE_DISCONNECTED, "conn_statu"));
|
}
|
|
if (state == ConnectState.NORMAL) {
|
resetCharacteristics();
|
}
|
}
|
};
|
|
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
|
@Override
|
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
super.onConnectionStateChange(gatt, status, newState);
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
mGatt = gatt;
|
}
|
}
|
|
@Override
|
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
super.onServicesDiscovered(gatt, status);
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
mHandler.post(() -> findAndEnableCharacteristics(gatt.getServices()));
|
}
|
}
|
|
@Override
|
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
super.onCharacteristicWrite(gatt, characteristic, status);
|
// Optional: Broadcast write status
|
}
|
|
@Override
|
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
super.onCharacteristicChanged(gatt, characteristic);
|
byte[] value = characteristic.getValue();
|
String hex = bytesToHexString(value);
|
// Broadcast received data
|
// You can define a custom event or interface for this
|
EventBus.getDefault().post(new UpdateEvent(UpdateEvent.Type.DEVICE_INFO, 0, hex));
|
}
|
};
|
|
private void findAndEnableCharacteristics(List<BluetoothGattService> services) {
|
// Simple logic: Find first Write and first Notify in the same service if possible
|
// Or just find any Write and any Notify
|
|
for (BluetoothGattService service : services) {
|
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
|
int props = characteristic.getProperties();
|
|
// Find Write Characteristic
|
if ((props & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 ||
|
(props & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
|
// TODO 待确认
|
if (writeCharacteristic == null && characteristic.getUuid().toString().startsWith("0000ff") ) {
|
writeCharacteristic = characteristic;
|
|
// Once we found a write char, let's look for a notify char in the SAME service
|
findNotifyInService(service);
|
|
if (notifyCharacteristic != null) {
|
// Found both in same service, we are good
|
return;
|
}
|
}
|
}
|
}
|
}
|
|
if (writeCharacteristic != null && notifyCharacteristic == null) {
|
// If we didn't find notify in the same service, look everywhere
|
for (BluetoothGattService service : services) {
|
findNotifyInService(service);
|
if (notifyCharacteristic != null) break;
|
}
|
}
|
|
if (writeCharacteristic != null) {
|
Toast.makeText(mContext, "服务已就绪 (Write: " + writeCharacteristic.getUuid().toString().substring(0,8) + ")", Toast.LENGTH_SHORT).show();
|
} else {
|
Toast.makeText(mContext, "未找到可写特征值", Toast.LENGTH_SHORT).show();
|
}
|
}
|
|
private void findNotifyInService(BluetoothGattService service) {
|
for (BluetoothGattCharacteristic c : service.getCharacteristics()) {
|
int props = c.getProperties();
|
if ((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0 ||
|
(props & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
|
notifyCharacteristic = c;
|
enableNotification(c);
|
return;
|
}
|
}
|
}
|
|
private void enableNotification(BluetoothGattCharacteristic characteristic) {
|
if (mGatt == null || characteristic == null) return;
|
|
boolean success = mGatt.setCharacteristicNotification(characteristic, true);
|
if (success) {
|
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
|
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
|
if (descriptor != null) {
|
int props = characteristic.getProperties();
|
byte[] value = null;
|
if ((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
|
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
} else if ((props & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
|
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
|
}
|
|
if (value != null) {
|
descriptor.setValue(value);
|
mGatt.writeDescriptor(descriptor);
|
}
|
}
|
}
|
}
|
|
public void sendCmd(String hexCmd) {
|
if (writeCharacteristic == null || mGatt == null) {
|
Toast.makeText(mContext, "蓝牙未连接或服务未找到", Toast.LENGTH_SHORT).show();
|
return;
|
}
|
|
byte[] data = hexStringToBytes(hexCmd);
|
if (data == null) {
|
Toast.makeText(mContext, "指令格式错误", Toast.LENGTH_SHORT).show();
|
return;
|
}
|
|
writeCharacteristic.setValue(data);
|
mGatt.writeCharacteristic(writeCharacteristic);
|
}
|
|
public static byte[] hexStringToBytes(String hexString) {
|
if (hexString == null || hexString.equals("")) {
|
return null;
|
}
|
hexString = hexString.toUpperCase();
|
hexString = hexString.replace(" ", "");
|
if (hexString.length() % 2 != 0) {
|
hexString = "0" + hexString;
|
}
|
|
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;
|
}
|
|
private static byte charToByte(char c) {
|
return (byte) "0123456789ABCDEF".indexOf(c);
|
}
|
|
public static String bytesToHexString(byte[] src) {
|
StringBuilder stringBuilder = new StringBuilder("");
|
if (src == null || src.length <= 0) {
|
return null;
|
}
|
for (int i = 0; i < src.length; i++) {
|
int v = src[i] & 0xFF;
|
String hv = Integer.toHexString(v);
|
if (hv.length() < 2) {
|
stringBuilder.append(0);
|
}
|
stringBuilder.append(hv);
|
}
|
return stringBuilder.toString().toUpperCase();
|
}
|
}
|