Skip to content

Commit a7e068d

Browse files
authored
Merge pull request #1 from neXenio/feature/notifications
Notifications
2 parents 8fdeac0 + f09caeb commit a7e068d

18 files changed

+479
-45
lines changed

app/src/main/java/com/nexenio/rxandroidbleserverapp/ExampleProfile.java

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,59 +9,103 @@
99
import com.nexenio.rxandroidbleserver.service.characteristic.CharacteristicBuilder;
1010
import com.nexenio.rxandroidbleserver.service.characteristic.RxBleCharacteristic;
1111
import com.nexenio.rxandroidbleserver.service.characteristic.descriptor.CharacteristicUserDescription;
12+
import com.nexenio.rxandroidbleserver.service.characteristic.descriptor.ClientCharacteristicConfiguration;
1213
import com.nexenio.rxandroidbleserver.service.characteristic.descriptor.DescriptorBuilder;
1314
import com.nexenio.rxandroidbleserver.service.characteristic.descriptor.RxBleDescriptor;
15+
import com.nexenio.rxandroidbleserver.service.value.BaseValue;
16+
import com.nexenio.rxandroidbleserver.service.value.RxBleValue;
1417

1518
import java.nio.ByteBuffer;
1619
import java.util.UUID;
20+
import java.util.concurrent.TimeUnit;
1721

1822
import androidx.annotation.NonNull;
23+
import io.reactivex.Completable;
24+
import io.reactivex.Observable;
1925

2026
public final class ExampleProfile {
2127

2228
public static final UUID EXAMPLE_SERVICE_UUID = UUID.fromString("31372aa1-015c-47fc-8283-2182ace15ef3");
2329
public static final UUID EXAMPLE_CHARACTERISTIC_UUID = UUID.fromString("aa4cd9de-087c-4951-bb92-4ea1cc8fd7d6");
2430
public static final UUID EXAMPLE_DESCRIPTOR_UUID = UUID.fromString("c8976395-2170-46c1-be55-b46d31dcb61c");
2531

26-
private ExampleProfile() {
32+
private RxBleServer exampleServer;
33+
private RxBleService exampleService;
34+
private RxBleCharacteristic exampleCharacteristic;
35+
private RxBleDescriptor exampleDescriptor;
2736

37+
public ExampleProfile(@NonNull Context context) {
38+
createExampleServer(context);
2839
}
2940

30-
public static RxBleServer createExampleServer(@NonNull Context context) {
31-
RxBleServer server = RxBleServerProvider.createServer(context);
32-
server.addService(createExampleService()).blockingAwait();
33-
return server;
41+
public Completable updateCharacteristicValues() {
42+
return Observable.interval(1, TimeUnit.SECONDS)
43+
.map(count -> (int) (count % 1337))
44+
.map(this::createExampleValue)
45+
.flatMapCompletable(value -> exampleCharacteristic.setValue(value)
46+
.andThen(exampleCharacteristic.sendNotifications()));
3447
}
3548

36-
private static RxBleService createExampleService() {
37-
return new ServiceBuilder(EXAMPLE_SERVICE_UUID)
49+
public RxBleServer getExampleServer() {
50+
return exampleServer;
51+
}
52+
53+
public RxBleService getExampleService() {
54+
return exampleService;
55+
}
56+
57+
public RxBleCharacteristic getExampleCharacteristic() {
58+
return exampleCharacteristic;
59+
}
60+
61+
public RxBleDescriptor getExampleDescriptor() {
62+
return exampleDescriptor;
63+
}
64+
65+
private RxBleServer createExampleServer(@NonNull Context context) {
66+
exampleServer = RxBleServerProvider.createServer(context);
67+
exampleServer.addService(createExampleService()).blockingAwait();
68+
return exampleServer;
69+
}
70+
71+
private RxBleService createExampleService() {
72+
exampleService = new ServiceBuilder(EXAMPLE_SERVICE_UUID)
3873
.withCharacteristic(createExampleCharacteristic())
3974
.isPrimaryService()
4075
.build();
76+
77+
return exampleService;
4178
}
4279

43-
private static RxBleCharacteristic createExampleCharacteristic() {
44-
return new CharacteristicBuilder(EXAMPLE_CHARACTERISTIC_UUID)
45-
.withInitialValue(ByteBuffer.allocate(4)
46-
.putInt(1337)
47-
.array())
80+
private RxBleCharacteristic createExampleCharacteristic() {
81+
exampleCharacteristic = new CharacteristicBuilder(EXAMPLE_CHARACTERISTIC_UUID)
82+
.withInitialValue(createExampleValue(0))
4883
.withDescriptor(new CharacteristicUserDescription("Example"))
84+
.withDescriptor(new ClientCharacteristicConfiguration())
4985
.withDescriptor(createExampleDescriptor())
5086
.allowRead()
5187
.allowWrite()
5288
.supportWritesWithoutResponse()
5389
.supportNotifications()
5490
.build();
91+
92+
return exampleCharacteristic;
5593
}
5694

57-
private static RxBleDescriptor createExampleDescriptor() {
58-
return new DescriptorBuilder(EXAMPLE_DESCRIPTOR_UUID)
59-
.withInitialValue(ByteBuffer.allocate(4)
60-
.putInt(42)
61-
.array())
95+
private RxBleDescriptor createExampleDescriptor() {
96+
exampleDescriptor = new DescriptorBuilder(EXAMPLE_DESCRIPTOR_UUID)
97+
.withInitialValue(createExampleValue(1337))
6298
.allowRead()
6399
.allowWrite()
64100
.build();
101+
102+
return exampleDescriptor;
103+
}
104+
105+
private RxBleValue createExampleValue(int number) {
106+
ByteBuffer buffer = ByteBuffer.allocate(4);
107+
buffer.putInt(number);
108+
return new BaseValue(buffer.array());
65109
}
66110

67111
}

app/src/main/java/com/nexenio/rxandroidbleserverapp/ExampleViewModel.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.nexenio.rxandroidbleserverapp;
22

33
import android.app.Application;
4-
import android.util.Log;
54

65
import com.nexenio.rxandroidbleserver.RxBleServer;
76

@@ -15,16 +14,17 @@
1514
import io.reactivex.disposables.CompositeDisposable;
1615
import io.reactivex.disposables.Disposable;
1716
import io.reactivex.schedulers.Schedulers;
17+
import timber.log.Timber;
1818

1919
public class ExampleViewModel extends AndroidViewModel {
2020

21-
private final String TAG = ExampleViewModel.class.getSimpleName();
22-
2321
private final CompositeDisposable viewModelDisposable = new CompositeDisposable();
2422

23+
private final ExampleProfile exampleProfile;
2524
private RxBleServer bleServer;
2625
private Disposable provideServicesDisposable;
2726
private Disposable advertiseServicesDisposable;
27+
private Disposable updateValueDisposable;
2828

2929
private MutableLiveData<Boolean> isProvidingServices = new MutableLiveData<>();
3030
private MutableLiveData<Boolean> isAdvertisingService = new MutableLiveData<>();
@@ -33,7 +33,9 @@ public class ExampleViewModel extends AndroidViewModel {
3333
public ExampleViewModel(@NonNull Application application) {
3434
super(application);
3535

36-
bleServer = ExampleProfile.createExampleServer(application);
36+
exampleProfile = new ExampleProfile(application);
37+
bleServer = exampleProfile.getExampleServer();
38+
3739
isProvidingServices.setValue(false);
3840
isAdvertisingService.setValue(false);
3941
}
@@ -61,25 +63,36 @@ public void toggleProvidingServices() {
6163
}
6264

6365
private void startProvidingServices() {
64-
Log.d(TAG, "Starting to provide services");
66+
Timber.d("Starting to provide services");
6567
provideServicesDisposable = bleServer.provideServices()
6668
.subscribeOn(Schedulers.io())
6769
.observeOn(AndroidSchedulers.mainThread())
6870
.doOnSubscribe(disposable -> isProvidingServices.postValue(true))
6971
.doFinally(() -> isProvidingServices.postValue(false))
7072
.subscribe(
71-
() -> Log.i(TAG, "Stopped providing services"),
73+
() -> Timber.i("Stopped providing services"),
74+
this::postError
75+
);
76+
77+
updateValueDisposable = exampleProfile.updateCharacteristicValues()
78+
.subscribeOn(Schedulers.io())
79+
.subscribe(
80+
() -> Timber.d("Done updating characteristic values"),
7281
this::postError
7382
);
7483

7584
viewModelDisposable.add(provideServicesDisposable);
85+
viewModelDisposable.add(updateValueDisposable);
7686
}
7787

7888
private void stopProvidingServices() {
79-
Log.d(TAG, "Stopping to provide services");
89+
Timber.d("Stopping to provide services");
8090
if (provideServicesDisposable != null && !provideServicesDisposable.isDisposed()) {
8191
provideServicesDisposable.dispose();
8292
}
93+
if (updateValueDisposable != null && !updateValueDisposable.isDisposed()) {
94+
updateValueDisposable.dispose();
95+
}
8396
}
8497

8598
/*
@@ -99,23 +112,23 @@ public void toggleAdvertisingServices() {
99112
}
100113

101114
private void startAdvertisingServices() {
102-
Log.d(TAG, "Starting to advertise services");
115+
Timber.d("Starting to advertise services");
103116
UUID uuid = ExampleProfile.EXAMPLE_SERVICE_UUID;
104117
advertiseServicesDisposable = bleServer.advertise(uuid)
105118
.subscribeOn(Schedulers.io())
106119
.observeOn(AndroidSchedulers.mainThread())
107120
.doOnSubscribe(disposable -> isAdvertisingService.postValue(true))
108121
.doFinally(() -> isAdvertisingService.postValue(false))
109122
.subscribe(
110-
() -> Log.i(TAG, "Stopped advertising services"),
123+
() -> Timber.i("Stopped advertising services"),
111124
this::postError
112125
);
113126

114127
viewModelDisposable.add(advertiseServicesDisposable);
115128
}
116129

117130
private void stopAdvertisingServices() {
118-
Log.d(TAG, "Stopping to advertise services");
131+
Timber.d("Stopping to advertise services");
119132
if (advertiseServicesDisposable != null && !advertiseServicesDisposable.isDisposed()) {
120133
advertiseServicesDisposable.dispose();
121134
}
@@ -126,7 +139,7 @@ private void stopAdvertisingServices() {
126139
*/
127140

128141
private void postError(@NonNull Throwable throwable) {
129-
Log.w(TAG, throwable);
142+
Timber.w(throwable);
130143
errors.postValue(throwable);
131144
}
132145

rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public class BaseServer implements RxBleServer, RxBleServerMapper {
7171
private final PublishSubject<RxBleClient> clientPublisher;
7272
private final PublishSubject<RxBleServerRequest> requestPublisher;
7373
private final PublishSubject<RxBleServerResponse> responsePublisher;
74+
private final PublishSubject<RxBleClient> clientNotifiedPublisher;
7475

7576
public BaseServer(Context context) {
7677
this.context = context;
@@ -79,6 +80,7 @@ public BaseServer(Context context) {
7980
clientPublisher = PublishSubject.create();
8081
requestPublisher = PublishSubject.create();
8182
responsePublisher = PublishSubject.create();
83+
clientNotifiedPublisher = PublishSubject.create();
8284

8385
requestPublisher.flatMapMaybe(this::createResponse)
8486
.subscribe(responsePublisher);
@@ -199,7 +201,7 @@ private Action createDisposeAction(AdvertiseCallback callback) {
199201

200202
@Override
201203
public Completable addService(@NonNull RxBleService service) {
202-
Completable modifyGattServer = getBluetoothGattServer()
204+
Completable modifyGattServer = getGattServer()
203205
.flatMapCompletable(bluetoothGattServer -> Completable.defer(() -> {
204206
BluetoothGattService bluetoothGattService = service.getGattService();
205207
boolean success = bluetoothGattServer.addService(bluetoothGattService);
@@ -217,14 +219,15 @@ public Completable addService(@NonNull RxBleService service) {
217219
return Completable.complete();
218220
}
219221
}).doOnComplete(() -> {
222+
service.setParentServer(this);
220223
services.add(service);
221224
Timber.d("Added service: %s", service);
222225
});
223226
}
224227

225228
@Override
226229
public Completable removeService(@NonNull RxBleService service) {
227-
Completable modifyGattServer = getBluetoothGattServer()
230+
Completable modifyGattServer = getGattServer()
228231
.flatMapCompletable(bluetoothGattServer -> Completable.defer(() -> {
229232
BluetoothGattService bluetoothGattService = service.getGattService();
230233
boolean success = bluetoothGattServer.removeService(bluetoothGattService);
@@ -272,6 +275,11 @@ public Observable<RxBleClient> observerClientConnectionStateChanges() {
272275
return clientPublisher;
273276
}
274277

278+
@Override
279+
public Observable<RxBleClient> observerClientNotifications() {
280+
return clientNotifiedPublisher;
281+
}
282+
275283
@Override
276284
public Single<RxBleClient> getClient(@NonNull BluetoothDevice bluetoothDevice) {
277285
return Observable.defer(() -> Observable.fromIterable(getClients()))
@@ -312,24 +320,25 @@ public Single<RxBleDescriptor> getDescriptor(@NonNull BluetoothGattDescriptor ga
312320
.firstOrError();
313321
}
314322

315-
private Single<BluetoothManager> getBluetoothManager() {
323+
@Override
324+
public Single<BluetoothGattServer> getGattServer() {
316325
return Single.defer(() -> {
317-
BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
318-
if (manager != null) {
319-
return Single.just(manager);
326+
if (bluetoothGattServer != null) {
327+
return Single.just(bluetoothGattServer);
320328
} else {
321-
return Single.error(new BluetoothNotAvailableException("Bluetooth system service not available"));
329+
return Single.error(new IllegalStateException("GATT server not available. " +
330+
"Make sure you have an active subscription to 'provideServices()'"));
322331
}
323332
});
324333
}
325334

326-
private Single<BluetoothGattServer> getBluetoothGattServer() {
335+
private Single<BluetoothManager> getBluetoothManager() {
327336
return Single.defer(() -> {
328-
if (bluetoothGattServer != null) {
329-
return Single.just(bluetoothGattServer);
337+
BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
338+
if (manager != null) {
339+
return Single.just(manager);
330340
} else {
331-
return Single.error(new IllegalStateException("GATT server not available. " +
332-
"Make sure you have an active subscription to 'provideServices()'"));
341+
return Single.error(new BluetoothNotAvailableException("Bluetooth system service not available"));
333342
}
334343
});
335344
}
@@ -380,6 +389,7 @@ private Completable bindServerCallback(@NonNull RxBleServerCallback callback) {
380389
callback.getCharacteristicWriteRequestPublisher().subscribe(requestPublisher);
381390
callback.getDescriptorReadRequestPublisher().subscribe(requestPublisher);
382391
callback.getDescriptorWriteRequestPublisher().subscribe(requestPublisher);
392+
callback.getClientNotifiedPublisher().subscribe(clientNotifiedPublisher);
383393
});
384394
}
385395

rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/RxBleServer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.nexenio.rxandroidbleserver;
22

3+
import android.bluetooth.BluetoothGattServer;
4+
35
import com.nexenio.rxandroidbleserver.client.RxBleClient;
46
import com.nexenio.rxandroidbleserver.service.RxBleService;
57

@@ -9,9 +11,12 @@
911
import androidx.annotation.NonNull;
1012
import io.reactivex.Completable;
1113
import io.reactivex.Observable;
14+
import io.reactivex.Single;
1215

1316
public interface RxBleServer {
1417

18+
Single<BluetoothGattServer> getGattServer();
19+
1520
Completable provideServices();
1621

1722
Completable provideServicesAndAdvertise(@NonNull UUID uuid);
@@ -32,4 +37,6 @@ public interface RxBleServer {
3237

3338
Observable<RxBleClient> observerClientConnectionStateChanges();
3439

40+
Observable<RxBleClient> observerClientNotifications();
41+
3542
}

rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/callback/BaseServerCallback.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class BaseServerCallback implements RxBleServerCallback {
1818
private PublishSubject<RxBleCharacteristicWriteRequest> characteristicWriteRequestPublisher;
1919
private PublishSubject<RxBleDescriptorReadRequest> descriptorReadRequestPublisher;
2020
private PublishSubject<RxBleDescriptorWriteRequest> descriptorWriteRequestPublisher;
21+
private PublishSubject<RxBleClient> clientNotifiedPublisher;
2122

2223
public BaseServerCallback() {
2324
clientConnectionStateChangePublisher = PublishSubject.create();
@@ -27,6 +28,7 @@ public BaseServerCallback() {
2728
characteristicWriteRequestPublisher = PublishSubject.create();
2829
descriptorReadRequestPublisher = PublishSubject.create();
2930
descriptorWriteRequestPublisher = PublishSubject.create();
31+
clientNotifiedPublisher = PublishSubject.create();
3032
}
3133

3234
@Override
@@ -64,4 +66,9 @@ public PublishSubject<RxBleDescriptorWriteRequest> getDescriptorWriteRequestPubl
6466
return descriptorWriteRequestPublisher;
6567
}
6668

69+
@Override
70+
public PublishSubject<RxBleClient> getClientNotifiedPublisher() {
71+
return clientNotifiedPublisher;
72+
}
73+
6774
}

rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/callback/RxBleServerCallback.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ public interface RxBleServerCallback {
2525

2626
PublishSubject<RxBleDescriptorWriteRequest> getDescriptorWriteRequestPublisher();
2727

28+
PublishSubject<RxBleClient> getClientNotifiedPublisher();
29+
2830
}

0 commit comments

Comments
 (0)