package io.trygvis.android.bt; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.util.Log; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Queue; import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicChanged; import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicRead; import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicWrite; import static io.trygvis.android.bt.BtActionExecutor.EventType.onConnectionStateChange; import static io.trygvis.android.bt.BtActionExecutor.EventType.onDescriptorRead; import static io.trygvis.android.bt.BtActionExecutor.EventType.onDescriptorWrite; import static io.trygvis.android.bt.BtActionExecutor.EventType.onFailure; import static io.trygvis.android.bt.BtActionExecutor.EventType.onReliableWriteCompleted; import static io.trygvis.android.bt.BtActionExecutor.EventType.onServicesDiscovered; public class BtActionExecutor { private final static String TAG = BtActionExecutor.class.getSimpleName(); private final Queue actionQ = new ArrayDeque<>(); private final Queue finallyQ = new ArrayDeque<>(); private final List remoteRssi = new ArrayList<>(); public BtActionExecutor() { } public BtActionExecutor(BtCallback first) { actionQ.add(first); } // public static final BtCallback waitForIt = new BtCallback("Wait for it") { // @Override // public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { // Log.w(TAG, "wait for it...!"); // return true; // } // }; // public synchronized BtActionExecutor onSuccess(BtCallback btCallback) { // actionQ.add(btCallback); // return this; // } public synchronized BtActionExecutor onConnectionStateChange(OnConnectionStateChange callback) { actionQ.add(new BtCallback("onConnectionStateChange") { @Override public boolean onConnectionStateChange(BluetoothGatt gatt, int newState) { return callback.onConnectionStateChange(gatt, newState); } }); return this; } public synchronized BtActionExecutor onServicesDiscovered(OnServicesDiscovered callback) { actionQ.add(new BtCallback("onServicesDiscovered") { @Override public boolean onServicesDiscovered(BluetoothGatt gatt) { return callback.onServicesDiscovered(gatt); } }); return this; } public synchronized BtActionExecutor onCharacteristicRead(OnCharacteristicRead callback) { actionQ.add(new BtCallback("onCharacteristicRead") { @Override public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { return callback.onCharacteristicRead(gatt, characteristic); } }); return this; } public synchronized BtActionExecutor onCharacteristicWrite(OnCharacteristicWrite callback) { actionQ.add(new BtCallback("onCharacteristicWrite") { @Override public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { return callback.onCharacteristicWrite(gatt, characteristic); } }); return this; } public synchronized BtActionExecutor onCharacteristicChanged(OnCharacteristicChanged callback) { actionQ.add(new BtCallback("onCharacteristicChanged") { @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { return callback.onCharacteristicChanged(gatt, characteristic); } }); return this; } public synchronized BtActionExecutor onDescriptorRead(OnDescriptorRead callback) { actionQ.add(new BtCallback("onDescriptorRead") { @Override public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) { return callback.onDescriptorRead(gatt, descriptor); } }); return this; } public synchronized BtActionExecutor onDescriptorWrite(OnDescriptorWrite callback) { actionQ.add(new BtCallback("onDescriptorWrite") { @Override public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) { return callback.onDescriptorWrite(gatt, descriptor); } }); return this; } public synchronized BtActionExecutor onReliableWriteCompleted(OnReliableWriteCompleted callback) { actionQ.add(new BtCallback("onReliableWriteCompleted") { @Override public boolean onReliableWriteCompleted(BluetoothGatt gatt) { return callback.onReliableWriteCompleted(gatt); } }); return this; } public synchronized BtActionExecutor onReadRemoteRssi(OnReadRemoteRssi callback) { actionQ.add(new BtCallback("onReadRemoteRssi") { @Override public boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi) { return callback.onReadRemoteRssi(gatt, rssi); } }); return this; } public synchronized BtActionExecutor onFailure(OnFailure callback) { actionQ.add(new BtCallback("onFailure") { @Override public void onFailure() { callback.onFailure(); } }); return this; } public static interface OnConnectionStateChange { boolean onConnectionStateChange(BluetoothGatt gatt, int newState); } public static interface OnServicesDiscovered { boolean onServicesDiscovered(BluetoothGatt gatt); } public static interface OnCharacteristicRead { boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); } public static interface OnCharacteristicWrite { boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); } public static interface OnCharacteristicChanged { boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); } public static interface OnDescriptorRead { boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor); } public static interface OnDescriptorWrite { boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor); } public static interface OnReliableWriteCompleted { boolean onReliableWriteCompleted(BluetoothGatt gatt); } public static interface OnReadRemoteRssi { boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi); } public static interface OnFailure { void onFailure(); } public synchronized BtActionExecutor addFinally(BtCallback btCallback) { finallyQ.add(btCallback); return this; } // public synchronized BtActionExecutor onRemoteRssi(BluetoothGattCallback callback) { // remoteRssi.add(callback); // return this; // } void onEvent(EventType key, String values, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int status, int newState) { if (status != BluetoothGatt.GATT_SUCCESS) { Log.w(TAG, "Operation failed: " + key + ", " + values); doFinally(); return; } Log.i(TAG, "Bt action completed successfully: callback=" + key); BtCallback btCallback; synchronized (this) { if (actionQ.isEmpty()) { Log.d(TAG, "All Bluetooth actions are done"); doFinally(); return; } try { Thread.sleep(1000); } catch (InterruptedException e) { // ignore } btCallback = actionQ.remove(); Log.i(TAG, "Executing bt action: " + btCallback.name); } try { boolean ok = callCallback(key, gatt, characteristic, descriptor, newState, btCallback); if (!ok) { Log.w(TAG, "The callback don't want to continue."); doFinally(); } if (actionQ.isEmpty()) { Log.i(TAG, "The queue is empty"); } } catch (NotOverriddenException e) { Log.w(TAG, "Unexpected callback by listener: " + key); doFinally(); } } private void doFinally() { actionQ.clear(); for (BtCallback callback = finallyQ.poll(); callback != null; callback = finallyQ.poll()) { try { callCallback(onFailure, null, null, null, 0, callback); } catch (NotOverriddenException e) { return; } } } enum EventType { onConnectionStateChange, onServicesDiscovered, onCharacteristicRead, onCharacteristicWrite, onCharacteristicChanged, onDescriptorRead, onDescriptorWrite, onReliableWriteCompleted, onFailure, } private Boolean callCallback(EventType key, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int newState, BtCallback btCallback) { switch (key) { case onConnectionStateChange: return btCallback.onConnectionStateChange(gatt, newState); case onServicesDiscovered: return btCallback.onServicesDiscovered(gatt); case onCharacteristicRead: return btCallback.onCharacteristicRead(gatt, characteristic); case onCharacteristicWrite: return btCallback.onCharacteristicWrite(gatt, characteristic); case onCharacteristicChanged: return btCallback.onCharacteristicChanged(gatt, characteristic); case onDescriptorRead: return btCallback.onDescriptorRead(gatt, descriptor); case onDescriptorWrite: return btCallback.onDescriptorWrite(gatt, descriptor); case onReliableWriteCompleted: return btCallback.onReliableWriteCompleted(gatt); case onFailure: btCallback.onFailure(); return null; default: Log.w(TAG, "Unknown callback: " + key); return null; } } public String toString() { StringBuilder s = new StringBuilder("Queue: "); Iterator it = actionQ.iterator(); int i = 0; while (it.hasNext()) { BtCallback c = it.next(); if (i > 0) { s.append(", "); } s.append(c.name); i++; } return s.toString(); } public BluetoothGattCallback asCallback() { return new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { onEvent(onConnectionStateChange, "status=" + status + ", newState=" + newState, gatt, null, null, status, newState); } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { onEvent(onServicesDiscovered, "status=" + status, gatt, null, null, status, 9); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { onEvent(onCharacteristicRead, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { onEvent(onCharacteristicWrite, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { onEvent(onCharacteristicChanged, "characteristic=" + characteristic.getUuid(), gatt, characteristic, null, BluetoothGatt.GATT_SUCCESS, 0); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { onEvent(onDescriptorRead, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { onEvent(onDescriptorWrite, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0); } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { onEvent(onReliableWriteCompleted, "status=" + status, gatt, null, null, status, 0); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { for (BluetoothGattCallback callback : remoteRssi) { callback.onReadRemoteRssi(gatt, rssi, status); } } }; } }