diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java new file mode 100644 index 000000000..76f0a839e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java @@ -0,0 +1,42 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; + +/** + * Base class for GattCallbacks wishing to just implement a few of the methods. + */ +public abstract class AbstractGattCallback implements GattCallback { + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + } + + @Override + public void onServicesDiscovered(BluetoothGatt gatt) { + } + + @Override + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + } + + @Override + public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + } + + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + } + + @Override + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + } + + @Override + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + } + + @Override + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java index 6f2c49741..9564695db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java @@ -9,6 +9,7 @@ import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.support.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,9 +20,9 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; -import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; +import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; /** * One queue/thread per connectable device. @@ -49,7 +50,7 @@ public final class BtLEQueue { private CountDownLatch mWaitForActionResultLatch; private CountDownLatch mConnectionLatch; private BluetoothGattCharacteristic mWaitCharacteristic; - private GattCallback mExternalGattCallback; + private final InternalGattCallback internalGattCallback; private Thread dispatchThread = new Thread("GadgetBridge GATT Dispatcher") { @@ -60,6 +61,8 @@ public final class BtLEQueue { while (!mDisposed && !mCrashed) { try { Transaction transaction = mTransactions.take(); + internalGattCallback.setTransactionGattCallback(null); + if (!isConnected()) { // TODO: request connection and initialization from the outside and wait until finished @@ -73,6 +76,7 @@ public final class BtLEQueue { mConnectionLatch = null; } + internalGattCallback.setTransactionGattCallback(transaction.getGattCallback()); mAbortTransaction = false; // Run all actions of the transaction until one doesn't succeed for (BtLEAction action : transaction.getActions()) { @@ -106,6 +110,7 @@ public final class BtLEQueue { } finally { mWaitForActionResultLatch = null; mWaitCharacteristic = null; + internalGattCallback.reset(); } } LOG.info("Queue Dispatch Thread terminated."); @@ -115,7 +120,7 @@ public final class BtLEQueue { public BtLEQueue(BluetoothAdapter bluetoothAdapter, GBDevice gbDevice, GattCallback externalGattCallback, Context context) { mBluetoothAdapter = bluetoothAdapter; mGbDevice = gbDevice; - mExternalGattCallback = externalGattCallback; + internalGattCallback = new InternalGattCallback(externalGattCallback); mContext = context; dispatchThread.start(); @@ -254,7 +259,25 @@ public final class BtLEQueue { // Implements callback methods for GATT events that the app cares about. For example, // connection change and services discovered. - private final BluetoothGattCallback internalGattCallback = new BluetoothGattCallback() { + private final class InternalGattCallback extends BluetoothGattCallback { + private @Nullable GattCallback mTransactionGattCallback; + private GattCallback mExternalGattCallback; + + public InternalGattCallback(GattCallback externalGattCallback) { + mExternalGattCallback = externalGattCallback; + } + + public void setTransactionGattCallback(@Nullable GattCallback callback) { + mTransactionGattCallback = callback; + } + + private GattCallback getCallbackToUse() { + if (mTransactionGattCallback != null) { + return mTransactionGattCallback; + } + return mExternalGattCallback; + } + @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { LOG.debug("connection state change, newState: " + newState + getStatusString(status)); @@ -293,9 +316,9 @@ public final class BtLEQueue { } if (status == BluetoothGatt.GATT_SUCCESS) { - if (mExternalGattCallback != null) { + if (getCallbackToUse() != null) { // only propagate the successful event - mExternalGattCallback.onServicesDiscovered(gatt); + getCallbackToUse().onServicesDiscovered(gatt); } } else { LOG.warn("onServicesDiscovered received: " + status); @@ -308,8 +331,8 @@ public final class BtLEQueue { if (!checkCorrectGattInstance(gatt, "characteristic write")) { return; } - if (mExternalGattCallback != null) { - mExternalGattCallback.onCharacteristicWrite(gatt, characteristic, status); + if (getCallbackToUse() != null) { + getCallbackToUse().onCharacteristicWrite(gatt, characteristic, status); } checkWaitingCharacteristic(characteristic, status); } @@ -322,8 +345,8 @@ public final class BtLEQueue { if (!checkCorrectGattInstance(gatt, "characteristic read")) { return; } - if (mExternalGattCallback != null) { - mExternalGattCallback.onCharacteristicRead(gatt, characteristic, status); + if (getCallbackToUse() != null) { + getCallbackToUse().onCharacteristicRead(gatt, characteristic, status); } checkWaitingCharacteristic(characteristic, status); } @@ -334,8 +357,8 @@ public final class BtLEQueue { if (!checkCorrectGattInstance(gatt, "descriptor read")) { return; } - if (mExternalGattCallback != null) { - mExternalGattCallback.onDescriptorRead(gatt, descriptor, status); + if (getCallbackToUse() != null) { + getCallbackToUse().onDescriptorRead(gatt, descriptor, status); } checkWaitingCharacteristic(descriptor.getCharacteristic(), status); } @@ -346,8 +369,8 @@ public final class BtLEQueue { if (!checkCorrectGattInstance(gatt, "descriptor write")) { return; } - if (mExternalGattCallback != null) { - mExternalGattCallback.onDescriptorWrite(gatt, descriptor, status); + if (getCallbackToUse() != null) { + getCallbackToUse().onDescriptorWrite(gatt, descriptor, status); } checkWaitingCharacteristic(descriptor.getCharacteristic(), status); } @@ -363,8 +386,8 @@ public final class BtLEQueue { LOG.info("Ignoring characteristic change event from wrong BluetoothGatt instance"); return; } - if (mExternalGattCallback != null) { - mExternalGattCallback.onCharacteristicChanged(gatt, characteristic); + if (getCallbackToUse() != null) { + getCallbackToUse().onCharacteristicChanged(gatt, characteristic); } } @@ -378,8 +401,8 @@ public final class BtLEQueue { LOG.info("Ignoring remote rssi event from wrong BluetoothGatt instance"); return; } - if (mExternalGattCallback != null) { - mExternalGattCallback.onReadRemoteRssi(gatt, rssi, status); + if (getCallbackToUse() != null) { + getCallbackToUse().onReadRemoteRssi(gatt, rssi, status); } } @@ -398,9 +421,13 @@ public final class BtLEQueue { } } } - }; - private String getStatusString(int status) { - return status == BluetoothGatt.GATT_SUCCESS ? " (success)" : " (failed: " + status + ")"; - } + private String getStatusString(int status) { + return status == BluetoothGatt.GATT_SUCCESS ? " (success)" : " (failed: " + status + ")"; + } + + public void reset() { + mTransactionGattCallback = null; + } + }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java index ffa1d91a9..4006e1cd9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle; +import android.support.annotation.Nullable; + import java.text.DateFormat; import java.util.ArrayList; import java.util.Collections; @@ -17,6 +19,7 @@ public class Transaction { private String mName; private List mActions = new ArrayList<>(4); private long creationTimestamp = System.currentTimeMillis(); + private @Nullable GattCallback gattCallback; public Transaction(String taskName) { this.mName = taskName; @@ -46,4 +49,15 @@ public class Transaction { public String toString() { return String.format(Locale.US, "%s: Transaction task: %s with %d actions", getCreationTime(), getTaskName(), mActions.size()); } + + public void setGattCallback(@Nullable GattCallback callback) { + gattCallback = callback; + } + + /** + * Returns the GattCallback for this transaction, or null if none. + */ + public @Nullable GattCallback getGattCallback() { + return gattCallback; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java index 77d9a338b..85eb3da6d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle; import android.bluetooth.BluetoothGattCharacteristic; +import android.support.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,6 +61,19 @@ public class TransactionBuilder { return this; } + /** + * Sets a GattCallback instance that will be called when the transaction is executed, + * resulting in GattCallback events. + * @param callback the callback to set, may be null + */ + public void setGattCallback(@Nullable GattCallback callback) { + mTransaction.setGattCallback(callback); + } + + public @Nullable GattCallback getGattCallback() { + return mTransaction.getGattCallback(); + } + /** * To be used as the final step to execute the transaction by the given queue. *