Skip to content

Commit

Permalink
Notifications implemented.
Browse files Browse the repository at this point in the history
  • Loading branch information
pauldemarco committed Jul 1, 2017
1 parent c419bef commit c60739b
Show file tree
Hide file tree
Showing 19 changed files with 410 additions and 116 deletions.
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ android {
// Required for local unit tests (JUnit 4 framework)
testCompile 'junit:junit:4.12'

compile "com.polidea.rxandroidble:rxandroidble:1.3.1"
compile "com.polidea.rxandroidble:rxandroidble:1.3.2"
//compile files('/home/paul/flutter/bin/cache/artifacts/engine/android-arm/flutter.jar')
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ public final static String getCharacteristicPath(String deviceId, String service
public final static String getCharacteristicMethodsPath(String deviceId, String serviceId, String charId){
return getCharacteristicPath(deviceId, serviceId, charId) + "methods";
}
public final static String getCharacteristicValuePath(String deviceId, String serviceId, String charId){
return getCharacteristicPath(deviceId, serviceId, charId) + "value";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ public String getStringValue() {
return new String(value);
}

public abstract Single<Byte[]> read();
public abstract Single<byte[]> read();

public abstract Single<Boolean> write(byte[] data);
public abstract Completable write(byte[] data);

public abstract Completable startUpdates();

public abstract Completable stopUpdates();
public abstract void stopUpdates();

public abstract Single<List<Descriptor>> getDescriptors();

Expand Down
12 changes: 12 additions & 0 deletions android/src/main/java/com/pauldemarco/flutterblue/Descriptor.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.pauldemarco.flutterblue;

import android.bluetooth.BluetoothGattDescriptor;

import java.util.HashMap;
import java.util.Map;

import rx.Completable;
import rx.Single;

Expand Down Expand Up @@ -27,4 +32,11 @@ public abstract class Descriptor {
public abstract Single<Byte[]> read();

public abstract Completable write(byte[] data);

public static Map<String, Object> toMap(BluetoothGattDescriptor descriptor) {
Map<String, Object> m = new HashMap<>();
m.put("id", new Guid(descriptor.getUuid()).toString());
m.put("value", descriptor.getValue());
return m;
}
}
4 changes: 4 additions & 0 deletions android/src/main/java/com/pauldemarco/flutterblue/Guid.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public String toMac() {
return mac.toUpperCase();
}

public UUID toUUID() {
return uuid;
}

public String toString() {
return uuid.toString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,63 @@
package com.pauldemarco.flutterblue.concrete;

import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.util.Log;

import com.pauldemarco.flutterblue.ChannelPaths;
import com.pauldemarco.flutterblue.Characteristic;
import com.pauldemarco.flutterblue.CharacteristicPropertyType;
import com.pauldemarco.flutterblue.CharacteristicWriteType;
import com.pauldemarco.flutterblue.Descriptor;
import com.pauldemarco.flutterblue.Device;
import com.pauldemarco.flutterblue.Guid;
import com.pauldemarco.flutterblue.Service;
import com.pauldemarco.flutterblue.utils.MyStreamHandler;
import com.polidea.rxandroidble.RxBleConnection;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import rx.Completable;
import rx.Observable;
import rx.Single;
import rx.subjects.PublishSubject;

/**
* Created by paul on 6/29/17.
*/

public class CharacteristicImpl extends Characteristic {
public class CharacteristicImpl extends Characteristic implements MethodCallHandler {
private static final String TAG = "CharacteristicImpl";

private final Registrar registrar;
private final MethodChannel methodChannel;
private final EventChannel valueChannel;
private final MyStreamHandler valueStream = new MyStreamHandler();
private final Observable<RxBleConnection> connectionObservable;
private final BluetoothGattCharacteristic nativeCharacteristic;
private final PublishSubject<Void> stopUpdatesTriggerSubject = PublishSubject.create();

public CharacteristicImpl(Registrar registrar, Guid guid, String name, int properties, CharacteristicWriteType writeType, boolean canRead, boolean canReadEncrypted, boolean canWrite, boolean canWriteEncrypted, Service service, Device device) {
public CharacteristicImpl(Registrar registrar, Guid guid, String name, int properties, CharacteristicWriteType writeType, boolean canRead, boolean canReadEncrypted, boolean canWrite, boolean canWriteEncrypted, Service service, Device device, Observable<RxBleConnection> connectionObservable, BluetoothGattCharacteristic nativeCharacteristic) {
super(guid, name, properties, writeType, canRead, canReadEncrypted, canWrite, canWriteEncrypted, service, device);
this.registrar = registrar;
this.methodChannel = new MethodChannel(registrar.messenger(), ChannelPaths.getCharacteristicMethodsPath(device.getGuid().toString(), service.getGuid().toString(), guid.toString()));
this.methodChannel.setMethodCallHandler(this);
this.valueChannel = new EventChannel(registrar.messenger(), ChannelPaths.getCharacteristicValuePath(device.getGuid().toString(), service.getGuid().toString(), guid.toString()));
this.valueChannel.setStreamHandler(valueStream);
this.connectionObservable = connectionObservable;
this.nativeCharacteristic = nativeCharacteristic;
}

public static CharacteristicImpl fromGattCharacteristic(Registrar registrar, BluetoothGattCharacteristic c, Service service, Device device) {
public static CharacteristicImpl fromGattCharacteristic(Registrar registrar, BluetoothGattCharacteristic c, Service service, Device device, Observable<RxBleConnection> connectionObservable) {
Guid guid = new Guid(c.getUuid());
String name = null; // TODO: Get name if UUID is a known characteristic (do this on dart side instead?)
int properties = c.getProperties();
Expand All @@ -55,27 +77,44 @@ public static CharacteristicImpl fromGattCharacteristic(Registrar registrar, Blu
boolean canReadEncrypted = (permissions & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED) == BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED;
boolean canWrite = (permissions & BluetoothGattCharacteristic.PERMISSION_WRITE) == BluetoothGattCharacteristic.PERMISSION_WRITE;
boolean canWriteEncrypted = (permissions & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED) == BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED;
return new CharacteristicImpl(registrar, guid, name, properties, writeType, canRead, canReadEncrypted, canWrite, canWriteEncrypted, service, device);
return new CharacteristicImpl(registrar, guid, name, properties, writeType, canRead, canReadEncrypted, canWrite, canWriteEncrypted, service, device, connectionObservable, c);
}

@Override
public Single<Byte[]> read() {
return null;
public Single<byte[]> read() {
return connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(guid.toUUID()))
.doOnNext(valueStream::onNext)
.first()
.toSingle();
}

@Override
public Single<Boolean> write(byte[] data) {
return null;
public Completable write(byte[] data) {
return connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(guid.toUUID(), data))
.toCompletable();
}

@Override
public Completable startUpdates() {
return null;
Observable o;
if((this.properties & CharacteristicPropertyType.NOTIFY) == CharacteristicPropertyType.NOTIFY){
o = connectionObservable.flatMap(rxBleConnection -> rxBleConnection.setupNotification(guid.toUUID()));
} else if((this.properties & CharacteristicPropertyType.INDICATE) == CharacteristicPropertyType.INDICATE) {
o = connectionObservable.flatMap(rxBleConnection -> rxBleConnection.setupIndication(guid.toUUID()));
} else {
return Completable.error(new Throwable("Characteristics does not have notify or indicate property enabled"));
}
Observable<byte[]> d = o.flatMap(notificationObservable -> notificationObservable);
d.takeUntil(stopUpdatesTriggerSubject)
.subscribe(valueStream::onNext, this::onReadError);
return Completable.fromObservable(o.first());
}

@Override
public Completable stopUpdates() {
return null;
public void stopUpdates() {
stopUpdatesTriggerSubject.onNext(null);
}

@Override
Expand All @@ -88,6 +127,51 @@ public Single<Descriptor> getDescriptor(Guid id) {
return null;
}

private void onReadError(Throwable t) {
Log.e(TAG, "onReadError: ", t);
}

@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("read")) {
read().subscribe(
bytes -> result.success(bytes),
throwable -> result.error("READ_ERROR", throwable.getMessage(), null)
);
} else if (call.method.equals("write")) {
byte[] data = (byte[])call.arguments;
write(data).subscribe(
() -> result.success(null),
throwable -> result.error("WRITE_ERROR", throwable.getMessage(), null)
);
} else if (call.method.equals("startUpdates")) {
startUpdates().subscribe(
() -> result.success(null),
throwable -> result.error("START_UPDATES_ERROR", throwable.getMessage(), null)
);
} else if (call.method.equals("stopUpdates")) {
stopUpdates();
result.success(null);
} else if (call.method.equals("getDescriptor")) {
String id = (String)call.arguments;
Guid guid = new Guid(id);
BluetoothGattDescriptor d = nativeCharacteristic.getDescriptor(guid.toUUID());
if(d != null) {
result.success(Descriptor.toMap(d));
} else {
result.error("GET_DESCRIPTOR_ERROR", "Descriptor not found with id " + guid.toString(), null);
}
} else if (call.method.equals("getDescriptors")) {
List<Map<String, Object>> res = new ArrayList<>();
for(BluetoothGattDescriptor d : nativeCharacteristic.getDescriptors()) {
res.add(Descriptor.toMap(d));
}
result.success(res);
} else {
result.notImplemented();
}
}

@Override
public Map<String, Object> toMap() {
Map<String, Object> m = new HashMap<>();
Expand Down Expand Up @@ -115,4 +199,6 @@ public boolean equals(Object o) {
public int hashCode() {
return this.guid.hashCode();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,6 @@ public void setAdvPacket(byte[] advPacket){
this.advPacket = advPacket;
}

public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("id", this.guid.toString());
map.put("name", this.name);
map.put("nativeDevice", null);
map.put("rssi", this.rssi);
map.put("state", this.state.ordinal());
map.put("advPacket", this.advPacket);
return map;
}

@Override
public boolean isConnected() {
return nativeDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.CONNECTED;
Expand Down Expand Up @@ -156,7 +145,7 @@ public Single<List<Service>> getServices() {
.map(RxBleDeviceServices::getBluetoothGattServices)
.flatMapIterable(services -> services)
.map(service -> {
Service s = new ServiceImpl(registrar, service, this);
Service s = new ServiceImpl(registrar, service, this, connectionObservable);
services.add(s); // add to local set
return s;
})
Expand All @@ -176,7 +165,7 @@ public Single<Service> getService(Guid id) {
.first() // Disconnect automatically after discovery
.map(RxBleDeviceServices::getBluetoothGattServices)
.flatMapIterable(services -> services)
.map(service -> (Service)new ServiceImpl(registrar, service, this))
.map(service -> (Service)new ServiceImpl(registrar, service, this, connectionObservable))
.filter(service -> service.getGuid() == id)
.toSingle();

Expand Down Expand Up @@ -269,6 +258,17 @@ public void onMethodCall(MethodCall call, Result result) {
}
}

public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("id", this.guid.toString());
map.put("name", this.name);
map.put("nativeDevice", null);
map.put("rssi", this.rssi);
map.put("state", this.state.ordinal());
map.put("advPacket", this.advPacket);
return map;
}

@Override
public boolean equals(Object o) {
if(this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.pauldemarco.flutterblue.Device;
import com.pauldemarco.flutterblue.Guid;
import com.pauldemarco.flutterblue.Service;
import com.polidea.rxandroidble.RxBleConnection;

import java.util.ArrayList;
import java.util.HashMap;
Expand All @@ -19,6 +20,7 @@
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import rx.Observable;
import rx.Single;

/**
Expand All @@ -29,26 +31,29 @@ public class ServiceImpl extends Service implements MethodCallHandler {

private final Registrar registrar;
private final MethodChannel methodChannel;
private final Observable<RxBleConnection> connectionObservable;

public ServiceImpl(Registrar registrar, Guid guid, Device device, boolean isPrimary) {
public ServiceImpl(Registrar registrar, Guid guid, Device device, boolean isPrimary, Observable<RxBleConnection> connectionObservable) {
super(guid, isPrimary, device);
this.registrar = registrar;
this.methodChannel = new MethodChannel(registrar.messenger(), ChannelPaths.getServiceMethodsPath(device.getGuid().toString(), guid.toString()));
this.connectionObservable = connectionObservable;
}

public ServiceImpl(Registrar registrar, BluetoothGattService service, Device device) {
public ServiceImpl(Registrar registrar, BluetoothGattService service, Device device, Observable<RxBleConnection> connectionObservable) {
this(
registrar,
new Guid(service.getUuid()),
device,
service.getType()==BluetoothGattService.SERVICE_TYPE_PRIMARY
service.getType()==BluetoothGattService.SERVICE_TYPE_PRIMARY,
connectionObservable
);
for(BluetoothGattService s : service.getIncludedServices()) {
ServiceImpl innerService = new ServiceImpl(registrar, s, device);
ServiceImpl innerService = new ServiceImpl(registrar, s, device, connectionObservable);
includedServices.add(innerService);
}
for(BluetoothGattCharacteristic nativeChar : service.getCharacteristics()) {
CharacteristicImpl c = CharacteristicImpl.fromGattCharacteristic(registrar, nativeChar, this, device);
CharacteristicImpl c = CharacteristicImpl.fromGattCharacteristic(registrar, nativeChar, this, device, connectionObservable);
characteristics.add(c);
}
}
Expand Down
Loading

0 comments on commit c60739b

Please sign in to comment.