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 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(); } }