package com.github.captain_miao.android.ble; import android.bluetooth.BluetoothGatt; import android.content.ComponentName; import android.content.Context; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import com.github.captain_miao.android.ble.constant.BleConnectState; import com.github.captain_miao.android.ble.constant.BleConstants; import com.github.captain_miao.android.ble.constant.ConnectError; import com.github.captain_miao.android.ble.utils.BleLog; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * @author YanLu * @since 2015-09-14 * * target: * 1. support ble connect status * 2. support message timeout * 3. support write、notify、read * */ public abstract class BluetoothHelper implements ServiceConnection, AppHandler.HandleMessageListener<BluetoothHelper> { private final static String TAG = BluetoothHelper.class.getName(); private long connectTimeout = 15000; protected Context context; protected BleCallback mBleCallback; private static AppHandler<BluetoothHelper> appHandler; public final Map<UUID, BleCallback> mCallbacks = new HashMap<>(); private Messenger mReceiveMessenger;//from BleService receive message private Messenger mSendMessage = null;//send message to BleService public BleConnectState mState = BleConnectState.INITIALED; public ConnectCallback mConnCallback; public TimeoutCallback mConnectTimeout; protected OnBindListener mBindListener; protected BluetoothGatt mGatt; public BluetoothHelper(Context context) { this.context = context; appHandler = new AppHandler<>(this, this); mReceiveMessenger = new Messenger(appHandler); } public BluetoothHelper(Context context, BleCallback bleCallback) { this(context); this.mBleCallback = bleCallback; } public void setBleCallback(BleCallback bleCallback) { this.mBleCallback = bleCallback; } public abstract boolean bindService(OnBindListener bindListener); public abstract void unbindService(); public boolean connectDevice(String mac, final ConnectCallback connectCallback) { if(mState.isServiceDiscovered()){ connectCallback.onConnectSuccess(); return true; } else { Message msg = Message.obtain(null, BleConstants.MSG_CONTROL_ID_CONNECT_MAC); msg.obj = mac; this.mConnCallback = connectCallback; if (mSendMessage != null) { try { appHandler.postDelayed(mConnectTimeout, connectTimeout); mSendMessage.send(msg); return true; } catch (RemoteException e) { BleLog.w(TAG, "Lost connection to service" + e.toString()); } } return false; } } public boolean startScanDevice() { return sendMsgWithoutSubscribe(BleConstants.MSG_CONTROL_ID_START_SCAN); } public boolean stopScanDevice() { return sendMsgWithoutSubscribe(BleConstants.MSG_CONTROL_ID_STOP_SCAN); } public boolean disconnectDevice() { return sendMsgWithoutSubscribe(BleConstants.MSG_CONTROL_ID_UNREGISTER); } public boolean writeCharacteristic(UUID serviceUUID, UUID CharacteristicUUID, byte[] values) { Message msg = Message.obtain(null, BleConstants.MSG_CONTROL_ID_WRITE_CHARACTERISTIC); if (msg != null && mSendMessage != null) { msg.obj = values; Bundle bundle = new Bundle(); bundle.putSerializable(BleConstants.BLE_MSG_SERVICE_UUID_KEY, serviceUUID); bundle.putSerializable(BleConstants.BLE_MSG_CHARACTERISTIC_UUID_KEY, CharacteristicUUID); msg.setData(bundle); try { //msg.replyTo = mReceiveMessenger; mSendMessage.send(msg); return true; } catch (RemoteException e) { BleLog.w(TAG, "Lost connection to service" + e.toString()); } } return false; } public boolean updateCharacteristicNotification(UUID serviceUUID, UUID characteristicUUID, UUID descriptorUUID, boolean enable) { Message msg = Message.obtain(null, BleConstants.MSG_CONTROL_ID_DESCRIPTOR_NOTIFICATION); if (msg != null && mSendMessage != null) { Bundle bundle = new Bundle(); bundle.putSerializable(BleConstants.BLE_MSG_SERVICE_UUID_KEY, serviceUUID); bundle.putSerializable(BleConstants.BLE_MSG_CHARACTERISTIC_UUID_KEY, characteristicUUID); bundle.putSerializable(BleConstants.BLE_MSG_DESCRIPTOR_UUID_KEY, descriptorUUID); bundle.putBoolean(BleConstants.BLE_MSG_ENABLE_KEY, enable); msg.setData(bundle); try { //msg.replyTo = mReceiveMessenger; mSendMessage.send(msg); return true; } catch (RemoteException e) { BleLog.w(TAG, "Lost connection to service" + e.toString()); } } return false; } public boolean readFromCharacteristic(UUID serviceUUID, UUID CharacteristicUUID) { Message msg = Message.obtain(null, BleConstants.MSG_CONTROL_ID_READ_CHARACTERISTIC); if (msg != null && mSendMessage != null) { Bundle bundle = new Bundle(); bundle.putSerializable(BleConstants.BLE_MSG_SERVICE_UUID_KEY, serviceUUID); bundle.putSerializable(BleConstants.BLE_MSG_CHARACTERISTIC_UUID_KEY, CharacteristicUUID); msg.setData(bundle); try { //msg.replyTo = mReceiveMessenger; mSendMessage.send(msg); return true; } catch (RemoteException e) { BleLog.w(TAG, "Lost connection to service" + e.toString()); } } return false; } public boolean sendMsgAndSubscribe(int msgId) { Message msg = Message.obtain(null, msgId); if (msg != null && mSendMessage != null) { try { msg.replyTo = mReceiveMessenger; mSendMessage.send(msg); return true; } catch (RemoteException e) { BleLog.w(TAG, "Lost connection to service" + e.toString()); } } return false; } public boolean sendMsgWithoutSubscribe(int msgId) { Message msg = Message.obtain(null, msgId); if (msg != null && mSendMessage != null) { try { mSendMessage.send(msg); return true; } catch (RemoteException e) { BleLog.w(TAG, "Lost connection to service" + e.toString()); } } return false; } @Override public void onHandleMessage(BluetoothHelper reference, Message msg) { Bundle data = msg.getData(); switch (msg.what) { case BleConstants.BLE_MSG_ID_CONNECTION_STATE_CHANGED: { BleConnectState newStatus = (BleConnectState) msg.obj; if (mBleCallback != null) { mBleCallback.onConnectionStateChange(mState.getCode(), newStatus.getCode()); } mState = newStatus; if (mConnCallback != null) { if (mState == BleConnectState.SERVICE_IS_DISCOVERED) { appHandler.removeCallbacks(mConnectTimeout); mConnCallback.onConnectSuccess(); } else if (mState == BleConnectState.SERVICE_IS_NOT_DISCOVERED) { appHandler.removeCallbacks(mConnectTimeout); mConnCallback.onConnectFailed(ConnectError.InvalidStatus); } else if (mState == BleConnectState.DISCONNECTED) { appHandler.removeCallbacks(mConnectTimeout); mConnCallback.onConnectFailed(ConnectError.ConnectTimeout); for (BleCallback callback : mCallbacks.values()) { if (callback != null) { callback.onFailed("ble disconnected..."); } } } } break; } case BleConstants.MSG_BLE_ID_CHARACTERISTIC_WRITE: { //onCharacteristicWrite BleLog.i(TAG, "MSG_BLE_ID_CHARACTERISTIC_WRITE"); if (data != null && mBleCallback != null) { UUID uuid = (UUID) data.getSerializable(BleConstants.BLE_MSG_CHARACTERISTIC_UUID_KEY); mBleCallback.onCharacteristicWrite(uuid, msg.arg1); } break; } case BleConstants.MSG_BLE_ID_DESCRIPTOR_WRITE: { //onDescriptorWrite BleLog.i(TAG, "MSG_BLE_ID_CHARACTERISTIC_WRITE"); if (data != null && mBleCallback != null) { UUID uuid = (UUID) data.getSerializable(BleConstants.BLE_MSG_CHARACTERISTIC_UUID_KEY); mBleCallback.onDescriptorWrite(uuid, msg.arg1); } break; } case BleConstants.MSG_BLE_ID_CHARACTERISTIC_NOTIFICATION: { //onCharacteristicChanged if (data != null && mBleCallback != null) { UUID uuid = (UUID) data.getSerializable(BleConstants.BLE_MSG_CHARACTERISTIC_UUID_KEY); mBleCallback.onCharacteristicNotification(uuid, (byte[]) msg.obj); } break; } case BleConstants.MSG_BLE_ID_CHARACTERISTIC_READ: { //onCharacteristicRead if (data != null && mBleCallback != null) { UUID uuid = (UUID) data.getSerializable(BleConstants.BLE_MSG_CHARACTERISTIC_UUID_KEY); mBleCallback.onCharacteristicRead(uuid, (byte[]) msg.obj); } break; } case BleConstants.MSG_BLE_ID_DESCRIPTOR_READ: { //onDescriptorRead if (data != null && mBleCallback != null) { UUID uuid = (UUID) data.getSerializable(BleConstants.BLE_MSG_CHARACTERISTIC_UUID_KEY); mBleCallback.onDescriptorRead(uuid, (byte[]) msg.obj); } break; } case BleConstants.MSG_BLE_ID_RELIABLE_WRITE_COMPLETED: { //onReliableWriteCompleted if(mBleCallback != null){ mBleCallback.onReliableWriteCompleted(msg.arg1); } break; } case BleConstants.MSG_BLE_ID_READ_REMOTE_RSSI: { //onReadRemoteRssi if(mBleCallback != null){ mBleCallback.onReadRemoteRssi(msg.arg2, msg.arg1); } break; } case BleConstants.MSG_BLE_ID_MTU_CHANGED: { //onMtuChanged if(mBleCallback != null){ mBleCallback.onMtuChanged(msg.arg2, msg.arg1); } break; } case BleConstants.MSG_BLE_ID_SERVICES_DISCOVERED: { //onServicesDiscovered mGatt = (BluetoothGatt) msg.obj; if (mBleCallback != null) { mBleCallback.onServicesDiscovered(mGatt, msg.arg1); } break; } } } @Override public void onServiceConnected(ComponentName name, IBinder service) { BleLog.i(TAG, "mConnection onServiceConnected"); mSendMessage = new Messenger(service); sendMsgAndSubscribe(BleConstants.MSG_CONTROL_ID_REGISTER); mConnectTimeout = new TimeoutCallback() { @Override public void onTimeout() { if(mConnCallback != null) { mConnCallback.onConnectFailed(ConnectError.ConnectTimeout); } stopScanDevice(); } }; if(mBindListener != null){ mBindListener.onServiceConnected(); } } @Override public void onServiceDisconnected(ComponentName name) { BleLog.i(TAG, "mConnection onServiceDisconnected"); mSendMessage = null; } public boolean isBinded(){ return mSendMessage != null; } public void release(){ sendMsgAndSubscribe(BleConstants.MSG_CONTROL_ID_UNREGISTER); if(isBinded()) { try { unbindService(); } catch (Exception e) { e.printStackTrace(); } } } public interface OnBindListener { void onServiceConnected(); } }