package io.trygvis.android.bt;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import java.util.Date;
import static io.trygvis.android.bt.BtPromise.BtBluetoothGattCallback;
import static io.trygvis.android.bt.BtPromise.PromiseResult.detour;
import static io.trygvis.android.bt.BtPromise.PromiseResult.stop;
public class BtDevice {
private final static String TAG = BtDevice.class.getSimpleName();
private final DefaultBtService btService;
private final BluetoothDevice bluetoothDevice;
private BluetoothGatt gatt;
private Integer rssi;
private BtScanResult scanResult;
private A tag;
private final String address;
private final long id;
private final boolean seenBefore;
private final Date firstSeen;
private Date lastSeen;
private boolean connected;
private final WrappingBluetoothGattCallback wrappingCallback = new WrappingBluetoothGattCallback();
private BtBluetoothGattCallback callback;
BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice, SQLiteDatabase db,
BtService.BtDbIntegration btDbIntegration, long id, Integer rssi,
BtScanResult scanResult, boolean seenBefore, Date firstSeen, Date lastSeen) {
this.btService = btService;
this.bluetoothDevice = bluetoothDevice;
this.id = id;
this.rssi = rssi;
this.scanResult = scanResult;
this.seenBefore = seenBefore;
this.firstSeen = firstSeen;
this.lastSeen = lastSeen;
this.tag = btDbIntegration.createTag(db, this);
this.address = bluetoothDevice.getAddress();
}
public long getId() {
return id;
}
public A getTag() {
return tag;
}
public String getAddress() {
return bluetoothDevice.getAddress();
}
public String getName() {
return bluetoothDevice.getName();
}
public Integer getRssi() {
return rssi;
}
public BtScanResult getScanResult() {
return scanResult;
}
public boolean isSeenBefore() {
return seenBefore;
}
public Date getFirstSeen() {
return firstSeen;
}
public Date getLastSeen() {
return lastSeen;
}
public void setLastSeen(Date lastSeen) {
this.lastSeen = lastSeen;
}
// public synchronized boolean connect(BtPromise executor) {
// Log.i(TAG, "connect(), address=" + bluetoothDevice.getAddress());
// BluetoothGattCallback callback = executor.asCallback(bluetoothDevice.getAddress());
// gatt = bluetoothDevice.connectGatt(btService, false, new WrappingBluetoothGattCallback(callback) {
// @Override
// public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
// BtDevice.this.connected = newState == BluetoothGatt.STATE_CONNECTED;
//
// super.onConnectionStateChange(gatt, status, newState);
// }
// });
// return true;
// }
/**
* The first handler must handle a onDirect().
*/
public synchronized void withConnection(BtPromise promise) {
if (callback != null) {
throw new RuntimeException("The current callback is not done.");
}
Log.i(TAG, "withConnection(), address=" + bluetoothDevice.getAddress() + ", connected: " + (gatt != null));
BtPromise newPromise;
if (gatt == null) {
newPromise = new BtPromise().
ignoreFailureForNext().
onConnectionStateChange((gatt, status, newState) -> {
Log.i(TAG, "defaultConnectCallback: status=" + status + ", newState=" + newState);
btService.sendBroadcast(btService.createDeviceConnection(address));
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) {
Log.i(TAG, "Connected to " + address);
return detour(promise);
} else {
Log.i(TAG, "Could not connect to " + address + ", trying again");
return detour(new BtPromise().onConnectionStateChange((gatt2, status2, newState2) -> {
if (status2 == BluetoothGatt.GATT_SUCCESS && newState2 == BluetoothGatt.STATE_CONNECTED) {
Log.i(TAG, "Connected to " + address);
return detour(promise);
}
Log.i(TAG, "Could still not connect to " + address + ", failing.");
return stop();
}));
}
});
} else {
newPromise = promise;
}
callback = newPromise.
onFinally(() -> {
Log.i(TAG, "Promise done, device is available again: address=" + address);
callback = null;
}).
asCallback(bluetoothDevice.getAddress());
if (gatt == null) {
gatt = bluetoothDevice.connectGatt(btService, false, wrappingCallback);
} else {
callback.onEvent(BtPromise.EventType.onDirect, "", gatt, null, null,
BluetoothGatt.GATT_SUCCESS,
BluetoothGatt.STATE_CONNECTED, gatt);
}
}
public boolean connected() {
return connected;
}
@Override
public String toString() {
return "BtDevice{address=" + bluetoothDevice.getAddress() + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BtDevice other = (BtDevice) o;
return getAddress().equals(other.getAddress());
}
@Override
public int hashCode() {
return getAddress().hashCode();
}
private class WrappingBluetoothGattCallback extends BluetoothGattCallback {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i(TAG, "Wrapping: onConnectionStateChange: " +
"address=" + gatt.getDevice().getAddress() + ", " +
"status=" + status + ", " +
"newState=" + newState);
boolean oldConnected = connected;
BtDevice.this.connected = status == BluetoothGatt.GATT_SUCCESS &&
newState == BluetoothGatt.STATE_CONNECTED;
try {
if (callback != null) {
callback.onConnectionStateChange(gatt, status, newState);
}
} finally {
if (!BtDevice.this.connected) {
if (oldConnected) {
Log.i(TAG, "Wrapper: Lost connection, removing gatt. gatt=" + gatt);
} else {
Log.i(TAG, "Wrapper: Lost connection, was not connected. gatt=" + gatt);
}
BtDevice.this.gatt = null;
} else {
Log.i(TAG, "Wrapper: connected");
}
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (callback != null) {
callback.onServicesDiscovered(gatt, status);
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (callback != null) {
callback.onCharacteristicRead(gatt, characteristic, status);
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (callback != null) {
callback.onCharacteristicWrite(gatt, characteristic, status);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (callback != null) {
callback.onCharacteristicChanged(gatt, characteristic);
}
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (callback != null) {
callback.onDescriptorRead(gatt, descriptor, status);
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (callback != null) {
callback.onDescriptorWrite(gatt, descriptor, status);
}
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
if (callback != null) {
callback.onReliableWriteCompleted(gatt, status);
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if (callback != null) {
callback.onReadRemoteRssi(gatt, rssi, status);
}
}
}
}