From 3e619a735e63a1222e71060d9e65b354a156b158 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Wed, 28 Jan 2015 23:45:38 +0100 Subject: o Major refactoring on the BtPromise, mainly internal. Renaming BtPromise to BtSequence and BtSequencer. --- .../java/io/trygvis/android/bt/BtSequencer.java | 265 +++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 bt/src/main/java/io/trygvis/android/bt/BtSequencer.java (limited to 'bt/src/main/java/io/trygvis/android/bt/BtSequencer.java') diff --git a/bt/src/main/java/io/trygvis/android/bt/BtSequencer.java b/bt/src/main/java/io/trygvis/android/bt/BtSequencer.java new file mode 100644 index 0000000..7eecbed --- /dev/null +++ b/bt/src/main/java/io/trygvis/android/bt/BtSequencer.java @@ -0,0 +1,265 @@ +package io.trygvis.android.bt; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.util.Log; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; + +import static io.trygvis.android.bt.BtSequence.ContinueDirectly; +import static io.trygvis.android.bt.BtSequence.Detour; +import static io.trygvis.android.bt.BtSequence.Fail; +import static io.trygvis.android.bt.BtSequence.SequenceResult; +import static io.trygvis.android.bt.BtSequence.Stop; +import static io.trygvis.android.bt.BtSequencer.EventType.onDirect; +import static io.trygvis.android.bt.BtSequencer.EventType.onFinally; + +class BtSequencer { + private final static String TAG = BtSequencer.class.getSimpleName(); + + enum EventType { + onConnectionStateChange, + onServicesDiscovered, + onCharacteristicRead, + onCharacteristicWrite, + onCharacteristicChanged, + onDescriptorRead, + onDescriptorWrite, + onReliableWriteCompleted, + + onReadRemoteRssi, + + onDirect, + onFailure, + onFinally, + } + + private final String address; + private final Deque sequences = new ArrayDeque<>(); + private final Deque> iterators = new ArrayDeque<>(); + + private Iterator callbacks; + private BtSequence sequence; + private List events = new ArrayList<>(); + + private boolean finallyDone; + + BtSequencer(String address, BtSequence sequence) { + this.address = address; + push(sequence); + } + + private void pop() { + sequence = sequences.pop(); + callbacks = iterators.pop(); + } + + private void push(BtSequence sequence) { + if (this.sequence != null) { + sequences.push(this.sequence); + iterators.push(this.callbacks); + } + + this.sequence = sequence; + this.callbacks = sequence.actionQ(); + } + + public void onDirect() { + onEvent(onDirect, "initial onDirect", null, null, null, BluetoothGatt.GATT_SUCCESS, + BluetoothGatt.STATE_CONNECTED); + } + + public synchronized void onEvent(EventType key, String values, BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, + int status, int newState) { + if (finallyDone) { + Log.w(TAG, "Got event after finally has been executed: " + key + "(" + values + ")."); + return; + } + + boolean success = status == BluetoothGatt.GATT_SUCCESS; + events.add(key + "(" + values + "), success=" + success); + + Log.i(TAG, "event: " + key + "(" + values + "), success=" + success); + + BtCallback callback; + synchronized (this) { + if (!callbacks.hasNext() && sequence.getNext().isPresent()) { + Log.i(TAG, "Switching to next sequence"); + + doFinally(success, sequence); + + sequence = sequence.getNext().get(); + callbacks = sequence.actionQ(); + } + + if (!callbacks.hasNext()) { + if (!sequences.isEmpty()) { + Log.d(TAG, "Sequence is done, continuing on previous sequence"); + doFinally(true, sequence); + pop(); + } else { + Log.d(TAG, "Sequence is done, no more sequences"); + doFinally(true); + return; + } + } + + callback = callbacks.next(); + + if (!success) { + if (callback.stopOnFailure) { + doFinally(false); + return; + } else { + Log.i(TAG, "Last status was a failure, but the callback still want it."); + } + } + } + + try { + Log.i(TAG, "Executing bt action: " + callback.type); + SequenceResult result = callCallback(key, gatt, characteristic, descriptor, status, newState, null, + callback); + + if (result instanceof Stop) { + Log.i(TAG, "The sequence stopped."); + doFinally(true); + return; + } + + if (result instanceof Fail) { + Log.i(TAG, "The sequence failed."); + doFinally(false); + return; + } + + if (result instanceof Detour) { + BtSequence detour = ((Detour) result).sequence; + Log.i(TAG, "Adding detour: " + detour); + + if (!detour.firstIsOnDirect()) { + throw new IllegalArgumentException("The first handler in a detour must be an onDirect."); + } + + events.add("detour, " + detour); + + push(detour); + onEvent(onDirect, "direct", gatt, null, null, BluetoothGatt.GATT_SUCCESS, 0); + return; + } + + if (result instanceof ContinueDirectly) { + onEvent(onDirect, "direct", gatt, null, null, BluetoothGatt.GATT_SUCCESS, 0); + return; + } + + if (!callbacks.hasNext()) { + if (!sequences.isEmpty()) { + Log.d(TAG, "Sequence is done, continuing on previous sequence"); + doFinally(true, sequence); + pop(); + } else { + Log.d(TAG, "Sequence is done, no more sequences"); + doFinally(true); + } + } + } catch (NotOverriddenException e) { + Log.w(TAG, "Unexpected callback by listener: " + key); +// doFailure(); + doFinally(false); + } catch (Exception e) { + Log.w(TAG, "Exception in callback", e); +// doFailure(); + doFinally(false); + } + } + + private void doFinally(boolean success) { + finallyDone = true; + showEvents(); + + doFinally(success, sequence); + + while (!sequences.isEmpty()) { + doFinally(success, sequences.pop()); + } + } + + private void doFinally(boolean success, BtSequence s) { + List q = s.finallyQ(); + + Log.w(TAG, "Executing " + q.size() + " finally handlers, success=" + success); + for (BtCallback callback : q) { + try { + callCallback(onFinally, null, null, null, 0, 0, success, callback); + } catch (NotOverriddenException e) { + // ignore + } + } + } + + private void showEvents() { + StringBuilder msg = new StringBuilder(); + + msg.append("Address: ").append(address).append("\n"); + +// msg.append("Event handlers: \n"); +// for (BtCallback cb : actionQ) { +// msg.append("- ").append(cb.name).append("\n"); +// } +// +// msg.append("Finally handlers: \n"); +// for (BtCallback cb : finallyQ) { +// msg.append("- ").append(cb.name).append("\n"); +// } + + msg.append("Events received: \n"); + for (String event : events) { + msg.append("- ").append(event).append("\n"); + } + + Log.w(TAG, msg.toString()); + } + + private static SequenceResult callCallback(EventType key, BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, + BluetoothGattDescriptor descriptor, int status, int newState, + Boolean success, BtCallback btCallback) { + switch (key) { + case onConnectionStateChange: + return btCallback.onConnectionStateChange(gatt, status, 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 onDirect: + return btCallback.onDirect(gatt); + case onFailure: + btCallback.onFailure(); + return null; + case onFinally: + btCallback.onFinally(success); + return null; + default: + Log.w(TAG, "Unknown callback: " + key); + return null; + } + } +} -- cgit v1.2.3