From 9ee4cb6f90f940d3963ef86d7da45a44edcc8f0b Mon Sep 17 00:00:00 2001 From: Steppschuh Date: Thu, 4 Jun 2020 13:57:51 +0200 Subject: [PATCH 1/6] Updated dependencies --- .idea/jarRepositories.xml | 30 ++++++++++++++++++++++++++++++ app/build.gradle | 4 ++-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .idea/jarRepositories.xml diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..eb2873e --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5de4014..5b55665 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,13 +26,14 @@ android { dependencies { implementation project(path: ':rxandroidbleserver') - implementation 'io.reactivex.rxjava3:rxjava:3.0.1' + implementation 'io.reactivex.rxjava3:rxjava:3.0.3' implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' implementation 'com.github.niklasvd96:rxpermissions:0.11.2' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'com.google.android.material:material:1.1.0' implementation 'com.jakewharton.timber:timber:4.7.1' @@ -41,5 +42,4 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - implementation 'com.google.android.material:material:1.0.0' } From ef325b12f0f8f4767ca9fbbe57f1086f6156d683 Mon Sep 17 00:00:00 2001 From: Steppschuh Date: Thu, 4 Jun 2020 13:58:36 +0200 Subject: [PATCH 2/6] Used requested offset when sending responses, fixes #3 --- .../nexenio/rxandroidbleserverapp/ExampleProfile.java | 9 +++++++-- .../java/com/nexenio/rxandroidbleserver/BaseServer.java | 3 ++- .../rxandroidbleserver/response/BaseServerResponse.java | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nexenio/rxandroidbleserverapp/ExampleProfile.java b/app/src/main/java/com/nexenio/rxandroidbleserverapp/ExampleProfile.java index 2b4d6c8..eec4eb9 100755 --- a/app/src/main/java/com/nexenio/rxandroidbleserverapp/ExampleProfile.java +++ b/app/src/main/java/com/nexenio/rxandroidbleserverapp/ExampleProfile.java @@ -16,6 +16,7 @@ import com.nexenio.rxandroidbleserver.service.value.RxBleValue; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -40,7 +41,7 @@ public ExampleProfile(@NonNull Context context) { public Completable updateCharacteristicValues() { return Observable.interval(1, TimeUnit.SECONDS) - .map(count -> (int) (count % 1337)) + .map(count -> "Updated example value #" + count) .map(this::createExampleValue) .flatMapCompletable(value -> exampleCharacteristic.setValue(value) .andThen(exampleCharacteristic.sendNotifications())); @@ -79,7 +80,7 @@ private RxBleService createExampleService() { private RxBleCharacteristic createExampleCharacteristic() { exampleCharacteristic = new CharacteristicBuilder(EXAMPLE_CHARACTERISTIC_UUID) - .withInitialValue(createExampleValue(0)) + .withInitialValue(createExampleValue("Initial example value")) .withDescriptor(new CharacteristicUserDescription("Example")) .withDescriptor(new ClientCharacteristicConfiguration()) .withDescriptor(createExampleDescriptor()) @@ -108,4 +109,8 @@ private RxBleValue createExampleValue(int number) { return new BaseValue(buffer.array()); } + private RxBleValue createExampleValue(String value) { + return new BaseValue(value.getBytes(StandardCharsets.UTF_8)); + } + } diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java index e1afadb..16c1759 100755 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java @@ -27,6 +27,7 @@ import com.nexenio.rxandroidbleserver.request.characteristic.RxBleCharacteristicWriteRequest; import com.nexenio.rxandroidbleserver.request.descriptor.RxBleDescriptorReadRequest; import com.nexenio.rxandroidbleserver.request.descriptor.RxBleDescriptorWriteRequest; +import com.nexenio.rxandroidbleserver.response.BaseServerResponse; import com.nexenio.rxandroidbleserver.response.RxBleServerResponse; import com.nexenio.rxandroidbleserver.service.RxBleService; import com.nexenio.rxandroidbleserver.service.characteristic.RxBleCharacteristic; @@ -436,7 +437,7 @@ private Completable sendResponse(RxBleServerResponse response) { response.getRequestId(), response.getStatus(), response.getOffset(), - response.getValue().getBytes() + BaseServerResponse.trimData(response.getValue().getBytes(), response.getOffset()) ); if (success) { diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/response/BaseServerResponse.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/response/BaseServerResponse.java index 2f01929..1b8e31f 100755 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/response/BaseServerResponse.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/response/BaseServerResponse.java @@ -79,6 +79,8 @@ public String toString() { public static byte[] trimData(byte[] data, int offset) { if (offset == 0) { return data; + } else if (offset >= data.length) { + return new byte[]{}; } else { return Arrays.copyOfRange(data, offset, data.length); } From ce0a8f76f6dc535f6e9bfe3c74f99fc04a395ead Mon Sep 17 00:00:00 2001 From: Steppschuh Date: Fri, 5 Jun 2020 11:22:55 +0200 Subject: [PATCH 3/6] Added method to configure if shared value provider or client value provider should be used --- .../characteristic/BaseCharacteristic.java | 2 +- .../service/value/BaseValueContainer.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/characteristic/BaseCharacteristic.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/characteristic/BaseCharacteristic.java index d307527..62403b5 100755 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/characteristic/BaseCharacteristic.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/characteristic/BaseCharacteristic.java @@ -27,8 +27,8 @@ import androidx.annotation.NonNull; import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.core.Maybe; -import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; import timber.log.Timber; diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/BaseValueContainer.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/BaseValueContainer.java index dbadf3f..4ce0cd3 100644 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/BaseValueContainer.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/BaseValueContainer.java @@ -114,4 +114,20 @@ public String toString() { '}'; } + public boolean isSharingValues() { + return shareValues; + } + + public void setShareValues(boolean shareValues) { + this.shareValues = shareValues; + } + + public RxBleSharedValueProvider getSharedValueProvider() { + return sharedValueProvider; + } + + public RxBleClientValueProvider getClientValueProvider() { + return clientValueProvider; + } + } From 36fbabd7827edbd145d546f3a7ab7f406028f570 Mon Sep 17 00:00:00 2001 From: Steppschuh Date: Fri, 5 Jun 2020 12:14:21 +0200 Subject: [PATCH 4/6] Added functionality to client value provider to better support value container --- .../service/value/BaseValueContainer.java | 52 +++++++++++++++-- .../service/value/RxBleValueContainer.java | 4 ++ .../provider/BaseClientValueProvider.java | 56 +++++++++++++++++++ .../provider/BaseSharedValueProvider.java | 7 +++ .../provider/RxBleClientValueProvider.java | 6 ++ 5 files changed, 121 insertions(+), 4 deletions(-) diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/BaseValueContainer.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/BaseValueContainer.java index 4ce0cd3..50db610 100644 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/BaseValueContainer.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/BaseValueContainer.java @@ -37,12 +37,21 @@ public Single getValue() { @Override public Completable setValue(@NonNull RxBleValue value) { - return sharedValueProvider.setValue(value); + return Completable.mergeArray( + sharedValueProvider.setValue(value), + clientValueProvider.setValueForAllClients(value) + ); } @Override public Observable getValueChanges() { - return sharedValueProvider.getValueChanges(); + return Observable.defer(() -> { + if (shareValues) { + return sharedValueProvider.getValueChanges(); + } else { + return clientValueProvider.getValueChangesFromAllClients(); + } + }); } @Override @@ -56,6 +65,17 @@ public Single getValue(@NonNull RxBleClient client) { }); } + @Override + public Observable getValuesFromAllClients() { + return Observable.defer(() -> { + if (shareValues) { + return sharedValueProvider.getValue().toObservable(); + } else { + return clientValueProvider.getValuesFromAllClients(); + } + }); + } + @Override public Completable setValue(@NonNull RxBleClient client, @NonNull RxBleValue value) { return Completable.defer(() -> { @@ -67,11 +87,33 @@ public Completable setValue(@NonNull RxBleClient client, @NonNull RxBleValue val }); } + @Override + public Completable setValueForAllClients(@NonNull RxBleValue value) { + return Completable.defer(() -> { + if (shareValues) { + return sharedValueProvider.setValue(value); + } else { + return clientValueProvider.setValueForAllClients(value); + } + }); + } + @Override public Observable getValueChanges(@NonNull RxBleClient client) { return clientValueProvider.getValueChanges(client); } + @Override + public Observable getValueChangesFromAllClients() { + return Observable.defer(() -> { + if (shareValues) { + return sharedValueProvider.getValueChanges(); + } else { + return clientValueProvider.getValueChangesFromAllClients(); + } + }); + } + public Single createReadRequestResponse(@NonNull RxBleReadRequest request) { return getValue(request.getClient()) .map(value -> new BaseServerResponse(request, value)) @@ -114,11 +156,13 @@ public String toString() { '}'; } - public boolean isSharingValues() { + @Override + public boolean isSharingValuesBetweenClients() { return shareValues; } - public void setShareValues(boolean shareValues) { + @Override + public void shareValuesBetweenClients(boolean shareValues) { this.shareValues = shareValues; } diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/RxBleValueContainer.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/RxBleValueContainer.java index 2efca68..3c4dd4c 100644 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/RxBleValueContainer.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/RxBleValueContainer.java @@ -16,4 +16,8 @@ public interface RxBleValueContainer extends RxBleClientValueProvider, RxBleShar Maybe createWriteRequestResponse(@NonNull RxBleWriteRequest request); + boolean isSharingValuesBetweenClients(); + + void shareValuesBetweenClients(boolean shareValues); + } diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseClientValueProvider.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseClientValueProvider.java index 62a52f8..961747b 100644 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseClientValueProvider.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseClientValueProvider.java @@ -4,23 +4,33 @@ import com.nexenio.rxandroidbleserver.exception.ValueNotAvailableException; import com.nexenio.rxandroidbleserver.service.value.RxBleValue; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.ReplaySubject; public class BaseClientValueProvider implements RxBleClientValueProvider { + private final ReplaySubject clientPublisher; + private final Map valueMap; private final Map> valuePublisherMap; + @Nullable + private RxBleValue valueForAllClients; + public BaseClientValueProvider() { this.valueMap = new HashMap<>(); this.valuePublisherMap = new HashMap<>(); + this.clientPublisher = ReplaySubject.create(); } @Override @@ -29,6 +39,10 @@ public Single getValue(@NonNull RxBleClient client) { synchronized (valueMap) { if (valueMap.containsKey(client)) { return Single.just(valueMap.get(client)); + } else if (valueForAllClients != null) { + // TODO: 05.06.20 check if we should do this instead of emitting an error + valueMap.put(client, valueForAllClients); + return Single.just(valueForAllClients); } else { return Single.error(new ValueNotAvailableException(client)); } @@ -36,21 +50,56 @@ public Single getValue(@NonNull RxBleClient client) { }); } + @Override + public Observable getValuesFromAllClients() { + return getCurrentClients().flatMapSingle(this::getValue); + } + @Override public Completable setValue(@NonNull RxBleClient client, @NonNull RxBleValue value) { return Completable.fromAction(() -> { + boolean isNewClient; synchronized (valueMap) { + isNewClient = !valueMap.containsKey(client); valueMap.put(client, value); } + if (isNewClient) { + clientPublisher.onNext(client); + } getOrCreateValuePublisher(client).onNext(value); }); } + @Override + public Completable setValueForAllClients(@NonNull RxBleValue value) { + return Completable.fromAction(() -> this.valueForAllClients = value) + .andThen(getCurrentClients().flatMapCompletable(client -> setValue(client, value))); + } + @Override public Observable getValueChanges(@NonNull RxBleClient client) { return getOrCreateValuePublisher(client); } + @Override + public Observable getValueChangesFromAllClients() { + return getCurrentAndFutureClients().flatMap(this::getValueChanges); + } + + private Observable getCurrentClients() { + return Observable.defer(() -> { + Collection clients; + synchronized (valueMap) { + clients = new HashSet<>(valueMap.keySet()); + } + return Observable.fromIterable(clients); + }); + } + + private Observable getCurrentAndFutureClients() { + return clientPublisher; + } + private PublishSubject getOrCreateValuePublisher(@NonNull RxBleClient client) { synchronized (valuePublisherMap) { if (valuePublisherMap.containsKey(client)) { @@ -63,4 +112,11 @@ private PublishSubject getOrCreateValuePublisher(@NonNull RxBleClien } } + @Override + public String toString() { + return "BaseClientValueProvider{" + + "valueMap=" + valueMap + + '}'; + } + } diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseSharedValueProvider.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseSharedValueProvider.java index 5d4d1b3..b82ce38 100644 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseSharedValueProvider.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseSharedValueProvider.java @@ -45,4 +45,11 @@ public Observable getValueChanges() { return valuePublisher; } + @Override + public String toString() { + return "BaseSharedValueProvider{" + + "value=" + value + + '}'; + } + } diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/RxBleClientValueProvider.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/RxBleClientValueProvider.java index 153a0a7..07dccef 100755 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/RxBleClientValueProvider.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/RxBleClientValueProvider.java @@ -12,8 +12,14 @@ public interface RxBleClientValueProvider { Single getValue(@NonNull RxBleClient client); + Observable getValuesFromAllClients(); + Completable setValue(@NonNull RxBleClient client, @NonNull RxBleValue value); + Completable setValueForAllClients(@NonNull RxBleValue value); + Observable getValueChanges(@NonNull RxBleClient client); + Observable getValueChangesFromAllClients(); + } From 1ad9bd4ca86dbe062cb5388a89d826f35db4d971 Mon Sep 17 00:00:00 2001 From: Steppschuh Date: Fri, 5 Jun 2020 12:15:27 +0200 Subject: [PATCH 5/6] Removed value for all clients in client value provider --- .../value/provider/BaseClientValueProvider.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseClientValueProvider.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseClientValueProvider.java index 961747b..0e49bc3 100644 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseClientValueProvider.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/service/value/provider/BaseClientValueProvider.java @@ -10,7 +10,6 @@ import java.util.Map; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; @@ -24,9 +23,6 @@ public class BaseClientValueProvider implements RxBleClientValueProvider { private final Map valueMap; private final Map> valuePublisherMap; - @Nullable - private RxBleValue valueForAllClients; - public BaseClientValueProvider() { this.valueMap = new HashMap<>(); this.valuePublisherMap = new HashMap<>(); @@ -39,10 +35,6 @@ public Single getValue(@NonNull RxBleClient client) { synchronized (valueMap) { if (valueMap.containsKey(client)) { return Single.just(valueMap.get(client)); - } else if (valueForAllClients != null) { - // TODO: 05.06.20 check if we should do this instead of emitting an error - valueMap.put(client, valueForAllClients); - return Single.just(valueForAllClients); } else { return Single.error(new ValueNotAvailableException(client)); } @@ -72,8 +64,7 @@ public Completable setValue(@NonNull RxBleClient client, @NonNull RxBleValue val @Override public Completable setValueForAllClients(@NonNull RxBleValue value) { - return Completable.fromAction(() -> this.valueForAllClients = value) - .andThen(getCurrentClients().flatMapCompletable(client -> setValue(client, value))); + return getCurrentClients().flatMapCompletable(client -> setValue(client, value)); } @Override From 6066014a19fcc98f5e1a7f327bd5af3c3513ccc9 Mon Sep 17 00:00:00 2001 From: Steppschuh Date: Thu, 11 Jun 2020 14:44:34 +0200 Subject: [PATCH 6/6] Removed subscribeOn from provideServicesAndAdvertise to avoid UndeliverableException Fixes #4 --- .../main/java/com/nexenio/rxandroidbleserver/BaseServer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java index 16c1759..a46bf05 100755 --- a/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java +++ b/rxandroidbleserver/src/main/java/com/nexenio/rxandroidbleserver/BaseServer.java @@ -136,8 +136,8 @@ public Completable provideServices() { @Override public Completable provideServicesAndAdvertise(@NonNull UUID uuid) { return Completable.mergeArray( - provideServices().subscribeOn(Schedulers.io()), - advertise(uuid).subscribeOn(Schedulers.io()) + provideServices(), + advertise(uuid) ); }