diff --git a/examples/example-android/androidBleSdkTestApp/app/build.gradle b/examples/example-android/androidBleSdkTestApp/app/build.gradle index 935f8154..961c3e62 100644 --- a/examples/example-android/androidBleSdkTestApp/app/build.gradle +++ b/examples/example-android/androidBleSdkTestApp/app/build.gradle @@ -23,6 +23,10 @@ android { dexOptions { javaMaxHeapSize "2g" } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { @@ -31,9 +35,9 @@ dependencies { implementation files('libs/polar-ble-sdk.aar') implementation files('libs/polar-protobuf-release.aar') implementation 'com.google.protobuf:protobuf-java:3.1.0' - implementation 'io.reactivex.rxjava2:rxjava:2.1.5' + implementation 'io.reactivex.rxjava3:rxjava:3.0.0' implementation group: 'commons-io', name: 'commons-io', version: '2.4' - implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' + implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' diff --git a/examples/example-android/androidBleSdkTestApp/app/src/main/java/polar/com/androidblesdk/MainActivity.java b/examples/example-android/androidBleSdkTestApp/app/src/main/java/polar/com/androidblesdk/MainActivity.java index f17c9cc7..3c718cac 100644 --- a/examples/example-android/androidBleSdkTestApp/app/src/main/java/polar/com/androidblesdk/MainActivity.java +++ b/examples/example-android/androidBleSdkTestApp/app/src/main/java/polar/com/androidblesdk/MainActivity.java @@ -1,13 +1,11 @@ package polar.com.androidblesdk; import android.Manifest; -import android.annotation.SuppressLint; import android.os.Build; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.util.Log; -import android.util.Pair; -import android.view.View; import android.widget.Button; import org.reactivestreams.Publisher; @@ -16,11 +14,9 @@ import java.util.Date; import java.util.UUID; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.functions.Action; -import io.reactivex.functions.Consumer; -import io.reactivex.functions.Function; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.functions.Function; import polar.com.sdk.api.PolarBleApi; import polar.com.sdk.api.PolarBleApiCallback; import polar.com.sdk.api.PolarBleApiDefaultImpl; @@ -28,9 +24,7 @@ import polar.com.sdk.api.model.PolarAccelerometerData; import polar.com.sdk.api.model.PolarDeviceInfo; import polar.com.sdk.api.model.PolarEcgData; -import polar.com.sdk.api.model.PolarExerciseData; import polar.com.sdk.api.model.PolarExerciseEntry; -import polar.com.sdk.api.model.PolarHrBroadcastData; import polar.com.sdk.api.model.PolarHrData; import polar.com.sdk.api.model.PolarOhrPPGData; import polar.com.sdk.api.model.PolarOhrPPIData; @@ -74,12 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { final Button H10RecordingStatus = this.findViewById(R.id.h10_recording_status); final Button setTime = this.findViewById(R.id.set_time); - api.setApiLogger(new PolarBleApi.PolarBleApiLogger() { - @Override - public void message(String s) { - Log.d(TAG,s); - } - }); + api.setApiLogger(s -> Log.d(TAG,s)); Log.d(TAG,"version: " + PolarBleApiDefaultImpl.versionInfo()); @@ -90,19 +79,19 @@ public void blePowerStateChanged(boolean powered) { } @Override - public void deviceConnected(PolarDeviceInfo polarDeviceInfo) { + public void deviceConnected(@NonNull PolarDeviceInfo polarDeviceInfo) { Log.d(TAG,"CONNECTED: " + polarDeviceInfo.deviceId); DEVICE_ID = polarDeviceInfo.deviceId; } @Override - public void deviceConnecting(PolarDeviceInfo polarDeviceInfo) { + public void deviceConnecting(@NonNull PolarDeviceInfo polarDeviceInfo) { Log.d(TAG,"CONNECTING: " + polarDeviceInfo.deviceId); DEVICE_ID = polarDeviceInfo.deviceId; } @Override - public void deviceDisconnected(PolarDeviceInfo polarDeviceInfo) { + public void deviceDisconnected(@NonNull PolarDeviceInfo polarDeviceInfo) { Log.d(TAG,"DISCONNECTED: " + polarDeviceInfo.deviceId); ecgDisposable = null; accDisposable = null; @@ -111,484 +100,250 @@ public void deviceDisconnected(PolarDeviceInfo polarDeviceInfo) { } @Override - public void ecgFeatureReady(String identifier) { + public void ecgFeatureReady(@NonNull String identifier) { Log.d(TAG,"ECG READY: " + identifier); // ecg streaming can be started now if needed } @Override - public void accelerometerFeatureReady(String identifier) { + public void accelerometerFeatureReady(@NonNull String identifier) { Log.d(TAG,"ACC READY: " + identifier); // acc streaming can be started now if needed } @Override - public void ppgFeatureReady(String identifier) { + public void ppgFeatureReady(@NonNull String identifier) { Log.d(TAG,"PPG READY: " + identifier); // ohr ppg can be started } @Override - public void ppiFeatureReady(String identifier) { + public void ppiFeatureReady(@NonNull String identifier) { Log.d(TAG,"PPI READY: " + identifier); // ohr ppi can be started } @Override - public void biozFeatureReady(String identifier) { + public void biozFeatureReady(@NonNull String identifier) { Log.d(TAG,"BIOZ READY: " + identifier); // ohr ppi can be started } @Override - public void hrFeatureReady(String identifier) { + public void hrFeatureReady(@NonNull String identifier) { Log.d(TAG,"HR READY: " + identifier); // hr notifications are about to start } @Override - public void disInformationReceived(String identifier, UUID uuid, String value) { + public void disInformationReceived(@NonNull String identifier,@NonNull UUID uuid,@NonNull String value) { Log.d(TAG,"uuid: " + uuid + " value: " + value); } @Override - public void batteryLevelReceived(String identifier, int level) { + public void batteryLevelReceived(@NonNull String identifier, int level) { Log.d(TAG,"BATTERY LEVEL: " + level); } @Override - public void hrNotificationReceived(String identifier, PolarHrData data) { + public void hrNotificationReceived(@NonNull String identifier,@NonNull PolarHrData data) { Log.d(TAG,"HR value: " + data.hr + " rrsMs: " + data.rrsMs + " rr: " + data.rrs + " contact: " + data.contactStatus + "," + data.contactStatusSupported); } @Override - public void polarFtpFeatureReady(String s) { + public void polarFtpFeatureReady(@NonNull String s) { Log.d(TAG,"FTP ready"); } }); - list.setOnClickListener(new View.OnClickListener() { - @SuppressLint("CheckResult") - @Override - public void onClick(View v) { - api.listExercises(DEVICE_ID).observeOn(AndroidSchedulers.mainThread()).subscribe( - new Consumer() { - @Override - public void accept(PolarExerciseEntry polarExerciseEntry) throws Exception { - Log.d(TAG,"next: " + polarExerciseEntry.date + " path: " + polarExerciseEntry.path + " id: " +polarExerciseEntry.identifier); - exerciseEntry = polarExerciseEntry; - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,"fetch exercises failed: " + throwable.getLocalizedMessage()); - } - }, - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"complete"); - } - } + list.setOnClickListener(v -> api.listExercises(DEVICE_ID).observeOn(AndroidSchedulers.mainThread()).subscribe( + polarExerciseEntry -> { + Log.d(TAG,"next: " + polarExerciseEntry.date + " path: " + polarExerciseEntry.path + " id: " +polarExerciseEntry.identifier); + exerciseEntry = polarExerciseEntry; + }, + throwable -> Log.e(TAG,"fetch exercises failed: " + throwable.getLocalizedMessage()), + () -> Log.d(TAG,"complete") + )); + + read.setOnClickListener(v -> { + if(exerciseEntry != null) { + api.fetchExercise(DEVICE_ID, exerciseEntry).observeOn(AndroidSchedulers.mainThread()).subscribe( + polarExerciseData -> Log.d(TAG,"exercise data count: " + polarExerciseData.hrSamples.size() + " samples: " + polarExerciseData.hrSamples), + throwable -> Log.e(TAG,"Failed to read exercise: " + throwable.getLocalizedMessage()) ); } }); - read.setOnClickListener(new View.OnClickListener() { - @SuppressLint("CheckResult") - @Override - public void onClick(View v) { - if(exerciseEntry != null) { - api.fetchExercise(DEVICE_ID, exerciseEntry).observeOn(AndroidSchedulers.mainThread()).subscribe( - new Consumer() { - @Override - public void accept(PolarExerciseData polarExerciseData) throws Exception { - Log.d(TAG,"exercise data count: " + polarExerciseData.hrSamples.size() + " samples: " + polarExerciseData.hrSamples); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,"Failed to read exercise: " + throwable.getLocalizedMessage()); - } - } - ); - } - } - }); - - remove.setOnClickListener(new View.OnClickListener() { - @SuppressLint("CheckResult") - @Override - public void onClick(View v) { - if(exerciseEntry != null) { - api.removeExercise(DEVICE_ID,exerciseEntry).observeOn(AndroidSchedulers.mainThread()).subscribe( - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"ex removed ok"); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.d(TAG,"ex remove failed: " + throwable.getLocalizedMessage()); - } - } - ); - } + remove.setOnClickListener(v -> { + if(exerciseEntry != null) { + api.removeExercise(DEVICE_ID,exerciseEntry).observeOn(AndroidSchedulers.mainThread()).subscribe( + () -> Log.d(TAG,"ex removed ok"), + throwable -> Log.d(TAG,"ex remove failed: " + throwable.getLocalizedMessage()) + ); } }); - broadcast.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if(broadcastDisposable == null) { - broadcastDisposable = api.startListenForPolarHrBroadcasts(null).subscribe( - new Consumer() { - @Override - public void accept(PolarHrBroadcastData polarBroadcastData) throws Exception { - Log.d(TAG,"HR BROADCAST " + - polarBroadcastData.polarDeviceInfo.deviceId + " HR: " + - polarBroadcastData.hr + " batt: " + - polarBroadcastData.batteryStatus); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,""+throwable.getLocalizedMessage()); - } - }, - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"complete"); - } - } - ); - }else{ - broadcastDisposable.dispose(); - broadcastDisposable = null; - } + broadcast.setOnClickListener(v -> { + if(broadcastDisposable == null) { + broadcastDisposable = api.startListenForPolarHrBroadcasts(null).subscribe( + polarBroadcastData -> Log.d(TAG,"HR BROADCAST " + + polarBroadcastData.polarDeviceInfo.deviceId + " HR: " + + polarBroadcastData.hr + " batt: " + + polarBroadcastData.batteryStatus), + throwable -> Log.e(TAG,""+throwable.getLocalizedMessage()), + () -> Log.d(TAG,"complete") + ); + }else{ + broadcastDisposable.dispose(); + broadcastDisposable = null; } }); - connect.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - try { - api.connectToDevice(DEVICE_ID); - } catch (PolarInvalidArgument polarInvalidArgument) { - polarInvalidArgument.printStackTrace(); - } + connect.setOnClickListener(v -> { + try { + api.connectToDevice(DEVICE_ID); + } catch (PolarInvalidArgument polarInvalidArgument) { + polarInvalidArgument.printStackTrace(); } }); - disconnect.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - try { - api.disconnectFromDevice(DEVICE_ID); - } catch (PolarInvalidArgument polarInvalidArgument) { - polarInvalidArgument.printStackTrace(); - } + disconnect.setOnClickListener(view -> { + try { + api.disconnectFromDevice(DEVICE_ID); + } catch (PolarInvalidArgument polarInvalidArgument) { + polarInvalidArgument.printStackTrace(); } }); - autoConnect.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if(autoConnectDisposable != null) { - autoConnectDisposable.dispose(); - autoConnectDisposable = null; - } - autoConnectDisposable = api.autoConnectToDevice(-50, "180D", null).subscribe( - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"auto connect search complete"); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,"" + throwable.toString()); - } - } - ); + autoConnect.setOnClickListener(view -> { + if(autoConnectDisposable != null) { + autoConnectDisposable.dispose(); + autoConnectDisposable = null; } + autoConnectDisposable = api.autoConnectToDevice(-50, "180D", null).subscribe( + () -> Log.d(TAG,"auto connect search complete"), + throwable -> Log.e(TAG,"" + throwable.toString()) + ); }); - ecg.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if(ecgDisposable == null) { - ecgDisposable = api.requestEcgSettings(DEVICE_ID).toFlowable().flatMap(new Function>() { - @Override - public Publisher apply(PolarSensorSetting polarEcgSettings) throws Exception { - PolarSensorSetting sensorSetting = polarEcgSettings.maxSettings(); - return api.startEcgStreaming(DEVICE_ID,sensorSetting); - } - }).subscribe( - new Consumer() { - @Override - public void accept(PolarEcgData polarEcgData) throws Exception { - for( Integer microVolts : polarEcgData.samples ){ - Log.d(TAG," yV: " + microVolts); - } - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG, ""+throwable.toString()); + ecg.setOnClickListener(v -> { + if(ecgDisposable == null) { + ecgDisposable = api.requestEcgSettings(DEVICE_ID).toFlowable().flatMap((Function>) polarEcgSettings -> { + PolarSensorSetting sensorSetting = polarEcgSettings.maxSettings(); + return api.startEcgStreaming(DEVICE_ID,sensorSetting); + }).subscribe( + polarEcgData -> { + for( Integer microVolts : polarEcgData.samples ){ + Log.d(TAG," yV: " + microVolts); } }, - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG, "complete"); - } - } - ); - } else { - // NOTE stops streaming if it is "running" - ecgDisposable.dispose(); - ecgDisposable = null; - } + throwable -> Log.e(TAG, ""+throwable.toString()), + () -> Log.d(TAG, "complete") + ); + } else { + // NOTE stops streaming if it is "running" + ecgDisposable.dispose(); + ecgDisposable = null; } }); - acc.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if(accDisposable == null) { - accDisposable = api.requestAccSettings(DEVICE_ID).toFlowable().flatMap(new Function>() { - @Override - public Publisher apply(PolarSensorSetting settings) throws Exception { - PolarSensorSetting sensorSetting = settings.maxSettings(); - return api.startAccStreaming(DEVICE_ID,sensorSetting); - } - }).observeOn(AndroidSchedulers.mainThread()).subscribe( - new Consumer() { - @Override - public void accept(PolarAccelerometerData polarAccelerometerData) throws Exception { - for( PolarAccelerometerData.PolarAccelerometerSample data : polarAccelerometerData.samples ){ - Log.d(TAG," x: " + data.x + " y: " + data.y + " z: "+ data.z); - } - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,""+throwable.getLocalizedMessage()); + acc.setOnClickListener(v -> { + if(accDisposable == null) { + accDisposable = api.requestAccSettings(DEVICE_ID).toFlowable().flatMap((Function>) settings -> { + PolarSensorSetting sensorSetting = settings.maxSettings(); + return api.startAccStreaming(DEVICE_ID,sensorSetting); + }).observeOn(AndroidSchedulers.mainThread()).subscribe( + polarAccelerometerData -> { + for( PolarAccelerometerData.PolarAccelerometerSample data : polarAccelerometerData.samples ){ + Log.d(TAG," x: " + data.x + " y: " + data.y + " z: "+ data.z); } }, - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"complete"); - } - } - ); - } else { - // NOTE dispose will stop streaming if it is "running" - accDisposable.dispose(); - accDisposable = null; - } + throwable -> Log.e(TAG,""+throwable.getLocalizedMessage()), + () -> Log.d(TAG,"complete") + ); + } else { + // NOTE dispose will stop streaming if it is "running" + accDisposable.dispose(); + accDisposable = null; } }); - ppg.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if(ppgDisposable == null) { - ppgDisposable = api.requestPpgSettings(DEVICE_ID).toFlowable().flatMap(new Function>() { - @Override - public Publisher apply(PolarSensorSetting polarPPGSettings) throws Exception { - return api.startOhrPPGStreaming(DEVICE_ID,polarPPGSettings.maxSettings()); - } - }).subscribe( - new Consumer() { - @Override - public void accept(PolarOhrPPGData polarOhrPPGData) throws Exception { - for( PolarOhrPPGData.PolarOhrPPGSample data : polarOhrPPGData.samples ){ - Log.d(TAG," ppg0: " + data.ppg0 + " ppg1: " + data.ppg1 + " ppg2: " + data.ppg2 + "ambient: " + data.ambient); - } - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,""+throwable.getLocalizedMessage()); + ppg.setOnClickListener(v -> { + if(ppgDisposable == null) { + ppgDisposable = api.requestPpgSettings(DEVICE_ID).toFlowable().flatMap(new Function>() { + @Override + public Publisher apply(PolarSensorSetting polarPPGSettings) throws Exception { + return api.startOhrPPGStreaming(DEVICE_ID,polarPPGSettings.maxSettings()); + } + }).subscribe( + polarOhrPPGData -> { + for( PolarOhrPPGData.PolarOhrPPGSample data : polarOhrPPGData.samples ){ + Log.d(TAG," ppg0: " + data.ppg0 + " ppg1: " + data.ppg1 + " ppg2: " + data.ppg2 + "ambient: " + data.ambient); } }, - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"complete"); - } - } - ); - } else { - ppgDisposable.dispose(); - ppgDisposable = null; - } + throwable -> Log.e(TAG,""+throwable.getLocalizedMessage()), + () -> Log.d(TAG,"complete") + ); + } else { + ppgDisposable.dispose(); + ppgDisposable = null; } }); - ppi.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if(ppiDisposable == null) { - ppiDisposable = api.startOhrPPIStreaming(DEVICE_ID).observeOn(AndroidSchedulers.mainThread()).subscribe( - new Consumer() { - @Override - public void accept(PolarOhrPPIData ppiData) throws Exception { - for(PolarOhrPPIData.PolarOhrPPISample sample : ppiData.samples) { - Log.d(TAG, "ppi: " + sample.ppi - + " blocker: " + sample.blockerBit + " errorEstimate: " + sample.errorEstimate); - } + ppi.setOnClickListener(v -> { + if(ppiDisposable == null) { + ppiDisposable = api.startOhrPPIStreaming(DEVICE_ID).observeOn(AndroidSchedulers.mainThread()).subscribe( + ppiData -> { + for(PolarOhrPPIData.PolarOhrPPISample sample : ppiData.samples) { + Log.d(TAG, "ppi: " + sample.ppi + + " blocker: " + sample.blockerBit + " errorEstimate: " + sample.errorEstimate); } }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,""+throwable.getLocalizedMessage()); - } - }, - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"complete"); - } - } - ); - } else { - ppiDisposable.dispose(); - ppiDisposable = null; - } - } - }); - - scan.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if(scanDisposable == null) { - scanDisposable = api.searchForDevice().observeOn(AndroidSchedulers.mainThread()).subscribe( - new Consumer() { - @Override - public void accept(PolarDeviceInfo polarDeviceInfo) throws Exception { - Log.d(TAG, "polar device found id: " + polarDeviceInfo.deviceId + " address: " + polarDeviceInfo.address + " rssi: " + polarDeviceInfo.rssi + " name: " + polarDeviceInfo.name + " isConnectable: " + polarDeviceInfo.isConnectable); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.d(TAG, "" + throwable.getLocalizedMessage()); - } - }, - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG, "complete"); - } - } - ); - }else{ - scanDisposable.dispose(); - scanDisposable = null; - } - } - }); - - startH10Recording.setOnClickListener(new View.OnClickListener() { - @SuppressLint("CheckResult") - @Override - public void onClick(View view) { - api.startRecording(DEVICE_ID,"TEST_APP_ID", PolarBleApi.RecordingInterval.INTERVAL_1S, PolarBleApi.SampleType.HR).subscribe( - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"recording started"); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,"recording start failed: " + throwable.getLocalizedMessage()); - } - } - ); - } - }); - - stopH10Recording.setOnClickListener(new View.OnClickListener() { - @SuppressLint("CheckResult") - @Override - public void onClick(View view) { - api.stopRecording(DEVICE_ID).subscribe( - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"recording stopped"); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG,"recording stop failed: " + throwable.getLocalizedMessage()); - } - } + throwable -> Log.e(TAG,""+throwable.getLocalizedMessage()), + () -> Log.d(TAG,"complete") ); + } else { + ppiDisposable.dispose(); + ppiDisposable = null; } }); - H10RecordingStatus.setOnClickListener(new View.OnClickListener() { - @SuppressLint("CheckResult") - @Override - public void onClick(View view) { - api.requestRecordingStatus(DEVICE_ID).subscribe( - new Consumer>() { - @Override - public void accept(Pair pair) throws Exception { - Log.d(TAG,"recording on: " + pair.first + " ID: " + pair.second); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.e(TAG, "recording status failed: " + throwable.getLocalizedMessage()); - } - } + scan.setOnClickListener(view -> { + if(scanDisposable == null) { + scanDisposable = api.searchForDevice().observeOn(AndroidSchedulers.mainThread()).subscribe( + polarDeviceInfo -> Log.d(TAG, "polar device found id: " + polarDeviceInfo.deviceId + " address: " + polarDeviceInfo.address + " rssi: " + polarDeviceInfo.rssi + " name: " + polarDeviceInfo.name + " isConnectable: " + polarDeviceInfo.isConnectable), + throwable -> Log.d(TAG, "" + throwable.getLocalizedMessage()), + () -> Log.d(TAG, "complete") ); + }else{ + scanDisposable.dispose(); + scanDisposable = null; } }); - setTime.setOnClickListener(new View.OnClickListener() { - @SuppressLint("CheckResult") - @Override - public void onClick(View v) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(new Date()); - api.setLocalTime(DEVICE_ID,calendar).subscribe( - new Action() { - @Override - public void run() throws Exception { - Log.d(TAG,"time set to device"); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - Log.d(TAG,"set time failed: " + throwable.getLocalizedMessage()); - } - }); - } + startH10Recording.setOnClickListener(view -> api.startRecording(DEVICE_ID,"TEST_APP_ID", PolarBleApi.RecordingInterval.INTERVAL_1S, PolarBleApi.SampleType.HR).subscribe( + () -> Log.d(TAG,"recording started"), + throwable -> Log.e(TAG,"recording start failed: " + throwable.getLocalizedMessage()) + )); + + stopH10Recording.setOnClickListener(view -> api.stopRecording(DEVICE_ID).subscribe( + () -> Log.d(TAG,"recording stopped"), + throwable -> Log.e(TAG,"recording stop failed: " + throwable.getLocalizedMessage()) + )); + + H10RecordingStatus.setOnClickListener(view -> api.requestRecordingStatus(DEVICE_ID).subscribe( + pair -> Log.d(TAG,"recording on: " + pair.first + " ID: " + pair.second), + throwable -> Log.e(TAG, "recording status failed: " + throwable.getLocalizedMessage()) + )); + + setTime.setOnClickListener(v -> { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date()); + api.setLocalTime(DEVICE_ID,calendar).subscribe( + () -> Log.d(TAG,"time set to device"), + throwable -> Log.d(TAG,"set time failed: " + throwable.getLocalizedMessage())); }); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && savedInstanceState == null) { diff --git a/sources/Android/android-communications/build.gradle b/sources/Android/android-communications/build.gradle index 8b879a02..bf4a5b3a 100755 --- a/sources/Android/android-communications/build.gradle +++ b/sources/Android/android-communications/build.gradle @@ -53,14 +53,15 @@ buildscript { } android { - compileSdkVersion 27 + compileSdkVersion 29 buildToolsVersion '28.0.3' defaultConfig { minSdkVersion 21 - targetSdkVersion 27 + targetSdkVersion 29 versionCode 13 versionName "13" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes.all { buildType -> def version = getVersion() @@ -79,6 +80,10 @@ android { lintOptions { abortOnError false } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } if ( project.hasProperty("artifactoryUser") && project.hasProperty("artifactoryPassword") ) { @@ -163,11 +168,13 @@ dependencies { implementation project(':polar-protobuf') implementation 'com.google.protobuf:protobuf-java:3.1.0' } - testImplementation 'junit:junit:4.12' - implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' - // Because RxAndroid releases are few and far between, it is recommended you also - // explicitly depend on RxJava's latest version for bug fixes and new features. - implementation 'io.reactivex.rxjava2:rxjava:2.2.0' + testImplementation 'junit:junit:4.13' + testImplementation "org.mockito:mockito-core:3.2.4" + testImplementation "io.mockk:mockk:1.9.3" + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' + implementation 'io.reactivex.rxjava3:rxjava:3.0.0' implementation 'commons-io:commons-io:2.4' - implementation 'com.android.support:support-annotations:24.2.1' + implementation 'com.android.support:support-annotations:28.0.0' } diff --git a/sources/Android/android-communications/src/androidTest/java/com/androidcommunications/polar/InstrumentedTests.kt b/sources/Android/android-communications/src/androidTest/java/com/androidcommunications/polar/InstrumentedTests.kt new file mode 100644 index 00000000..81e5e6dc --- /dev/null +++ b/sources/Android/android-communications/src/androidTest/java/com/androidcommunications/polar/InstrumentedTests.kt @@ -0,0 +1,178 @@ +package com.androidcommunications.polar + +import android.content.Context +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import com.androidcommunications.polar.api.ble.model.BleDeviceSession +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.BDDeviceSessionImpl +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ConnectionHandler +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ConnectionHandlerObserver +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ConnectionInterface +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ScannerInterface +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +import io.mockk.impl.annotations.MockK +import junit.framework.TestCase + +@RunWith(AndroidJUnit4::class) +class InstrumentedTests { + + private lateinit var targetContext: Context + private lateinit var connectionHandler: ConnectionHandler + + @MockK + private lateinit var connectionInterface: ConnectionInterface + + @MockK + private lateinit var scannerInterface: ScannerInterface + + @MockK + private lateinit var connectionHandlerObserver: ConnectionHandlerObserver + + @Before + fun setUp() { + targetContext = InstrumentationRegistry.getInstrumentation().targetContext + connectionHandler = ConnectionHandler(targetContext, connectionInterface, scannerInterface, connectionHandlerObserver) + } + + @Test + fun testConnectionHandler_1() { + val bleDeviceSession = BDDeviceSessionImpl() + connectionHandler.connectDevice(bleDeviceSession, true) + connectionHandler.deviceConnected(bleDeviceSession) + TestCase.assertSame(bleDeviceSession.sessionState, BleDeviceSession.DeviceSessionState.SESSION_OPEN) + } + + @Test + fun testConnectionHandler_2() { + val bleDeviceSession = BDDeviceSessionImpl() + connectionHandler.connectDevice(bleDeviceSession, true); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + connectionHandler.deviceDisconnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + connectionHandler.advertisementHeadReceived(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + connectionHandler.deviceConnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + } + + @Test + fun testConnectionHandler_3() { + val bleDeviceSession = BDDeviceSessionImpl() + val bleDeviceSession2 = BDDeviceSessionImpl() + + // multi connections + connectionHandler.connectDevice(bleDeviceSession, true); + connectionHandler.connectDevice(bleDeviceSession2, true); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + connectionHandler.deviceConnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + connectionHandler.advertisementHeadReceived(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + connectionHandler.deviceConnected(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + + // multi disconnection + connectionHandler.disconnectDevice(bleDeviceSession); + connectionHandler.disconnectDevice(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); + connectionHandler.deviceDisconnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); + connectionHandler.deviceDisconnected(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + + // multi connect / disconnect + bleDeviceSession2.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); + connectionHandler.connectDevice(bleDeviceSession, true); + connectionHandler.disconnectDevice(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); + connectionHandler.deviceConnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); + connectionHandler.deviceDisconnected(bleDeviceSession2); + + // maintaining multi connections + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); + bleDeviceSession2.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); + connectionHandler.deviceDisconnected(bleDeviceSession); + connectionHandler.deviceDisconnected(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + connectionHandler.advertisementHeadReceived(bleDeviceSession); + connectionHandler.advertisementHeadReceived(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + connectionHandler.deviceConnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + connectionHandler.advertisementHeadReceived(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + connectionHandler.deviceConnected(bleDeviceSession2); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + TestCase.assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + + // same state tests + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); + connectionHandler.connectDevice(bleDeviceSession, true); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + connectionHandler.connectDevice(bleDeviceSession, true); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + connectionHandler.connectDevice(bleDeviceSession, true); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + connectionHandler.deviceConnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + connectionHandler.connectDevice(bleDeviceSession, true); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); + + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + connectionHandler.disconnectDevice(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); + connectionHandler.disconnectDevice(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); + connectionHandler.disconnectDevice(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); + connectionHandler.deviceDisconnected(bleDeviceSession); + + // + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + // TODO nonConnectable[0] = true; + connectionHandler.connectDevice(bleDeviceSession, true); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + connectionHandler.advertisementHeadReceived(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + // TODO nonConnectable[0] = false; + connectionHandler.advertisementHeadReceived(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); + connectionHandler.deviceConnected(bleDeviceSession); + + // + connectionHandler.disconnectDevice(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); + connectionHandler.connectDevice(bleDeviceSession, true); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + connectionHandler.deviceDisconnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + connectionHandler.connectDevice(bleDeviceSession, false); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); + connectionHandler.setAutomaticReconnection(false); + connectionHandler.deviceDisconnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); + connectionHandler.setAutomaticReconnection(true); + connectionHandler.deviceDisconnected(bleDeviceSession); + TestCase.assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + } +} \ No newline at end of file diff --git a/sources/Android/android-communications/src/androidTest/java/com/androidcommunications/polar/PolarRefTest.java b/sources/Android/android-communications/src/androidTest/java/com/androidcommunications/polar/PolarRefTest.java deleted file mode 100755 index a25b4b6f..00000000 --- a/sources/Android/android-communications/src/androidTest/java/com/androidcommunications/polar/PolarRefTest.java +++ /dev/null @@ -1,521 +0,0 @@ -package com.androidcommunications.polar; - -import android.bluetooth.BluetoothDevice; - -import com.androidcommunications.polar.api.ble.model.BleDeviceSession; -import com.androidcommunications.polar.enpoints.ble.common.BleDeviceSession2; -import com.androidcommunications.polar.enpoints.ble.common.attribute.AttributeOperation; -import com.androidcommunications.polar.enpoints.ble.common.connection.ConnectionHandler; -import com.androidcommunications.polar.enpoints.ble.common.connection.ConnectionInterface; -import com.androidcommunications.polar.enpoints.ble.common.connection.ScannerInterface; - -import junit.framework.TestCase; - -import java.util.List; -import java.util.UUID; - -import io.reactivex.Completable; -import io.reactivex.Single; -import io.reactivex.functions.Action; - -public class PolarRefTest extends TestCase { - - - public void testConnectionHandler1() { - ConnectionHandler connectionHandler = new ConnectionHandler( - new ConnectionInterface() { - @Override - public void connectDevice(BleDeviceSession2 session) { - - } - - @Override - public void disconnectDevice(BleDeviceSession2 session) { - - } - - @Override - public void cancelDeviceConnection(BleDeviceSession2 session) { - - } - }, - new ScannerInterface() { - @Override - public void connectionHandlerResumeScanning() { - - } - - @Override - public void connectionHandlerRequestStopScanning() { - - } - - }); - - BleDeviceSession2 bleDeviceSession = new BleDeviceSession2() { - - @Override - public boolean sendNextAttributeOperation(AttributeOperation attributeOperation) { - return false; - } - - @Override - public void discoverServices() { - - } - - @Override - public void handleDisconnection() { - - } - - @Override - public void startAuthentication(Action complete) { - - } - - @Override - public void reset() { - - } - - @Override - public boolean isNonConnectableAdvertisement() { - return false; - } - - @Override - public String getAddress() { - return ""; - } - - @Override - public Completable authenticate() { - return null; - } - - @Override - public boolean isAuthenticated() { - return false; - } - - @Override - public Single> monitorServicesDiscovered(boolean checkConnection) { - return null; - } - - @Override - public boolean clearGattCache() { - return false; - } - - @Override - public Single readRssiValue() { - return null; - } - - @Override - public BluetoothDevice getBluetoothDevice() { - return null; - } - - }; - - connectionHandler.connectDevice(bleDeviceSession, true); - connectionHandler.deviceConnected(bleDeviceSession); - - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - } - - public void testConnectionHandler2() { - ConnectionHandler connectionHandler = new ConnectionHandler( - new ConnectionInterface() { - @Override - public void connectDevice(BleDeviceSession2 session) { - - } - - @Override - public void disconnectDevice(BleDeviceSession2 session) { - - } - - @Override - public void cancelDeviceConnection(BleDeviceSession2 session) { - - } - }, - new ScannerInterface() { - @Override - public void connectionHandlerResumeScanning() { - - } - - @Override - public void connectionHandlerRequestStopScanning() { - - } - }); - - BleDeviceSession2 bleDeviceSession = new BleDeviceSession2() { - - @Override - public boolean sendNextAttributeOperation(AttributeOperation attributeOperation) { - return false; - } - - @Override - public void discoverServices() { - - } - - @Override - public void handleDisconnection() { - - } - - @Override - public void startAuthentication(Action complete) { - - } - - @Override - public void reset() { - - } - - @Override - public boolean isNonConnectableAdvertisement() { - return false; - } - - @Override - public String getAddress() { - return ""; - } - - @Override - public Completable authenticate() { - return null; - } - - @Override - public boolean isAuthenticated() { - return false; - } - - @Override - public Single> monitorServicesDiscovered(boolean checkConnection) { - return null; - } - - @Override - public boolean clearGattCache() { - return false; - } - - @Override - public Single readRssiValue() { - return null; - } - - @Override - public BluetoothDevice getBluetoothDevice() { - return null; - } - }; - - connectionHandler.connectDevice(bleDeviceSession, true); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - connectionHandler.deviceDisconnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - connectionHandler.advertisementHeadReceived(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - connectionHandler.deviceConnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - } - - public void testConnectionHandler3() { - - final boolean[] nonConnectable = new boolean[1]; - nonConnectable[0] = false; - - final BleDeviceSession2 bleDeviceSession = new BleDeviceSession2() { - - @Override - public boolean sendNextAttributeOperation(AttributeOperation attributeOperation) { - return false; - } - - @Override - public void discoverServices() { - - } - - @Override - public void handleDisconnection() { - - } - - @Override - public void startAuthentication(Action complete) { - - } - - @Override - public void reset() { - - } - - @Override - public boolean isNonConnectableAdvertisement() { - return nonConnectable[0]; - } - - @Override - public String getAddress() { - return ""; - } - - @Override - public Completable authenticate() { - return null; - } - - @Override - public boolean isAuthenticated() { - return false; - } - - @Override - public Single> monitorServicesDiscovered(boolean checkConnection) { - return null; - } - - @Override - public boolean clearGattCache() { - return false; - } - - @Override - public Single readRssiValue() { - return null; - } - - @Override - public BluetoothDevice getBluetoothDevice() { - return null; - } - }; - - final BleDeviceSession2 bleDeviceSession2 = new BleDeviceSession2() { - - @Override - public boolean sendNextAttributeOperation(AttributeOperation attributeOperation) { - return false; - } - - @Override - public void discoverServices() { - - } - - @Override - public void handleDisconnection() { - - } - - @Override - public void startAuthentication(Action complete) { - - } - - @Override - public void reset() { - - } - - @Override - public boolean isNonConnectableAdvertisement() { - return false; - } - - @Override - public String getAddress() { - return ""; - } - - @Override - public Completable authenticate() { - return null; - } - - @Override - public boolean isAuthenticated() { - return false; - } - - @Override - public Single> monitorServicesDiscovered(boolean checkConnection) { - return null; - } - - @Override - public boolean clearGattCache() { - return false; - } - - @Override - public Single readRssiValue() { - return null; - } - - @Override - public BluetoothDevice getBluetoothDevice() { - return null; - } - }; - - ConnectionHandler connectionHandler = new ConnectionHandler( - new ConnectionInterface() { - @Override - public void connectDevice(BleDeviceSession2 session) { - - } - - @Override - public void disconnectDevice(BleDeviceSession2 session) { - - } - - @Override - public void cancelDeviceConnection(BleDeviceSession2 session) { - - } - }, - new ScannerInterface() { - @Override - public void connectionHandlerResumeScanning() { - - } - - @Override - public void connectionHandlerRequestStopScanning() { - - } - }); - - // multi connections - connectionHandler.connectDevice(bleDeviceSession, true); - connectionHandler.connectDevice(bleDeviceSession2, true); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - connectionHandler.deviceConnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - connectionHandler.advertisementHeadReceived(bleDeviceSession2); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - connectionHandler.deviceConnected(bleDeviceSession2); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - - // multi disconnection - connectionHandler.disconnectDevice(bleDeviceSession); - connectionHandler.disconnectDevice(bleDeviceSession2); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); - connectionHandler.deviceDisconnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); - connectionHandler.deviceDisconnected(bleDeviceSession2); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - - // multi connect / disconnect - bleDeviceSession2.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); - connectionHandler.connectDevice(bleDeviceSession, true); - connectionHandler.disconnectDevice(bleDeviceSession2); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); - connectionHandler.deviceConnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); - connectionHandler.deviceDisconnected(bleDeviceSession2); - - // maintaining multi connections - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); - bleDeviceSession2.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); - connectionHandler.deviceDisconnected(bleDeviceSession); - connectionHandler.deviceDisconnected(bleDeviceSession2); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - connectionHandler.advertisementHeadReceived(bleDeviceSession); - connectionHandler.advertisementHeadReceived(bleDeviceSession2); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - connectionHandler.deviceConnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - connectionHandler.advertisementHeadReceived(bleDeviceSession2); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - connectionHandler.deviceConnected(bleDeviceSession2); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - assertSame(bleDeviceSession2.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - - // same state tests - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); - connectionHandler.connectDevice(bleDeviceSession, true); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - connectionHandler.connectDevice(bleDeviceSession, true); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - connectionHandler.connectDevice(bleDeviceSession, true); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - connectionHandler.deviceConnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - connectionHandler.connectDevice(bleDeviceSession, true); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN); - - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - connectionHandler.disconnectDevice(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); - connectionHandler.disconnectDevice(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); - connectionHandler.disconnectDevice(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); - connectionHandler.deviceDisconnected(bleDeviceSession); - - // - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - nonConnectable[0] = true; - connectionHandler.connectDevice(bleDeviceSession, true); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - connectionHandler.advertisementHeadReceived(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - nonConnectable[0] = false; - connectionHandler.advertisementHeadReceived(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPENING); - connectionHandler.deviceConnected(bleDeviceSession); - - // - connectionHandler.disconnectDevice(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSING); - connectionHandler.connectDevice(bleDeviceSession, true); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - connectionHandler.deviceDisconnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - connectionHandler.connectDevice(bleDeviceSession, false); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); - connectionHandler.setAutomaticReconnection(false); - connectionHandler.deviceDisconnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - bleDeviceSession.setSessionState(BleDeviceSession.DeviceSessionState.SESSION_OPEN); - connectionHandler.setAutomaticReconnection(true); - connectionHandler.deviceDisconnected(bleDeviceSession); - assertSame(bleDeviceSession.getSessionState(), BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - - } -} diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/BleDeviceListener.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/BleDeviceListener.java index d51bc35a..23a07c09 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/BleDeviceListener.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/BleDeviceListener.java @@ -10,20 +10,14 @@ import com.androidcommunications.polar.api.ble.model.advertisement.BleAdvertisementContent; import com.androidcommunications.polar.api.ble.model.gatt.BleGattBase; import com.androidcommunications.polar.api.ble.model.gatt.BleGattFactory; -import com.androidcommunications.polar.api.ble.model.gatt.client.BleHrClient; -import com.androidcommunications.polar.api.ble.model.gatt.client.BlePMDClient; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.List; import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.TimeUnit; -import io.reactivex.Flowable; -import io.reactivex.Observable; -import io.reactivex.disposables.Disposable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Observable; public abstract class BleDeviceListener { @@ -60,12 +54,6 @@ protected BleDeviceListener(Set > clients) { */ abstract public boolean bleActive(); - /** - * @return flowable stream of ble state - */ - @Deprecated - abstract public Flowable monitorBleState(); - /** * @param cb callback */ @@ -117,24 +105,12 @@ public interface BlePowerStateChangedCallback { abstract public void openSessionDirect(BleDeviceSession session); /** - * aquire connection establishment + * aquire connection establishment, BleDeviceSessionStateChangedCallback callbacks are invoked * @param session device * @param uuids needed uuids to be found from advertisement data, when reconnecting */ abstract public void openSessionDirect(BleDeviceSession session, List uuids); - /** - * Deprecated use setDeviceSessionStateChangedCallback instead - * - * Produces: onNext: When a device session state has changed, Note use pair.second to check the state (see BleDeviceSession.DeviceSessionState)
- * onError: should not be produced
- * onCompleted: If stream is further configured
- * @param session, a specific session or null = monitor all sessions - * @return Observable stream - */ - @Deprecated - abstract public Observable> monitorDeviceSessionState(BleDeviceSession session); - public interface BleDeviceSessionStateChangedCallback { /** * Invoked for all sessions and all state changes diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/BleRefHostApiFactory.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/BleRefHostApiFactory.java deleted file mode 100644 index fcc83f2e..00000000 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/BleRefHostApiFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.androidcommunications.polar.api.ble; - -public class BleRefHostApiFactory { - - private static BleRefHostApiFactory instance = new BleRefHostApiFactory(); - private BleDeviceListener deviceListener; - - private BleRefHostApiFactory(){ - } - - public static BleRefHostApiFactory getInstance(){ - return instance; - } - - public void setDeviceListener(BleDeviceListener deviceListener){ - this.deviceListener = deviceListener; - } - - public BleDeviceListener getDeviceListener(){ - return deviceListener; - } -} diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/BleDeviceSession.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/BleDeviceSession.java index 53f034fe..0247ec0d 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/BleDeviceSession.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/BleDeviceSession.java @@ -16,11 +16,11 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; -import io.reactivex.Completable; -import io.reactivex.Flowable; -import io.reactivex.Single; -import io.reactivex.annotations.NonNull; -import io.reactivex.functions.Function; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.functions.Function; /** * Bluetooth le device class, contains all essential api's for sufficient usage of bluetooth le device diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/BleGattBase.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/BleGattBase.java index 8b3ecedd..b015886f 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/BleGattBase.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/BleGattBase.java @@ -7,18 +7,17 @@ import com.androidcommunications.polar.common.ble.AtomicSet; import java.util.HashMap; -import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.Completable; -import io.reactivex.CompletableEmitter; -import io.reactivex.CompletableOnSubscribe; -import io.reactivex.Flowable; -import io.reactivex.Scheduler; -import io.reactivex.schedulers.Schedulers; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.CompletableEmitter; +import io.reactivex.rxjava3.core.CompletableOnSubscribe; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Scheduler; +import io.reactivex.rxjava3.schedulers.Schedulers; /** * Container class holding information from current client or service diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleBattClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleBattClient.java index 940b2e54..ed795aee 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleBattClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleBattClient.java @@ -8,16 +8,14 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReferenceArray; -import io.reactivex.BackpressureStrategy; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; -import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.functions.Action; +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; +import io.reactivex.rxjava3.core.FlowableOnSubscribe; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.core.SingleEmitter; +import io.reactivex.rxjava3.core.SingleOnSubscribe; public class BleBattClient extends BleGattBase { @@ -47,18 +45,8 @@ public void processServiceData(UUID characteristic, final byte[] data, int statu if(status == 0){ if(characteristic.equals(BATTERY_LEVEL_CHARACTERISTIC)){ batteryLevel.set(data[0]); - RxUtils.emitNext(observers, new RxUtils.Emitter>() { - @Override - public void item(SingleEmitter object) { - object.onSuccess((int) data[0]); - } - }); - RxUtils.emitNext(notificationObservers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext((int)data[0]); - } - }); + RxUtils.emitNext(observers, object -> object.onSuccess((int) data[0])); + RxUtils.emitNext(notificationObservers, object -> object.onNext((int)data[0])); } } } @@ -83,25 +71,17 @@ public String toString() { */ public Single waitBatteryLevelUpdate(final boolean checkConnection){ final SingleEmitter[] observer = new SingleEmitter[1]; - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter subscriber) { - if(!checkConnection || txInterface.isConnected()){ - observer[0] = subscriber; - observers.add(subscriber); - if(batteryLevel.get()!=-1){ - subscriber.onSuccess(batteryLevel.get()); - } - }else if(!subscriber.isDisposed()) { - subscriber.tryOnError(new BleDisconnected()); + return Single.create((SingleOnSubscribe) subscriber -> { + if(!checkConnection || txInterface.isConnected()){ + observer[0] = subscriber; + observers.add(subscriber); + if(batteryLevel.get()!=-1){ + subscriber.onSuccess(batteryLevel.get()); } + }else if(!subscriber.isDisposed()) { + subscriber.tryOnError(new BleDisconnected()); } - }).doFinally(new Action() { - @Override - public void run() { - observers.remove(observer[0]); - } - }); + }).doFinally(() -> observers.remove(observer[0])); } /** @@ -111,25 +91,17 @@ public void run() { */ public Flowable monitorBatteryLevelUpdate(final boolean checkConnection) { final FlowableEmitter[] observer = new FlowableEmitter[1]; - return Flowable.create(new FlowableOnSubscribe() { - @Override - public void subscribe(FlowableEmitter emitter) throws Exception { - if(!checkConnection || txInterface.isConnected()){ - observer[0] = emitter; - notificationObservers.add(emitter); - if(batteryLevel.get()!=-1){ - emitter.onNext(batteryLevel.get()); - } - }else if(!emitter.isCancelled()) { - emitter.tryOnError(new BleDisconnected()); + return Flowable.create((FlowableOnSubscribe) emitter -> { + if(!checkConnection || txInterface.isConnected()){ + observer[0] = emitter; + notificationObservers.add(emitter); + if(batteryLevel.get()!=-1){ + emitter.onNext(batteryLevel.get()); } + }else if(!emitter.isCancelled()) { + emitter.tryOnError(new BleDisconnected()); } - }, BackpressureStrategy.LATEST).doFinally(new Action() { - @Override - public void run() throws Exception { - notificationObservers.remove(observer[0]); - } - }); + }, BackpressureStrategy.LATEST).doFinally(() -> notificationObservers.remove(observer[0])); } /** @@ -142,22 +114,14 @@ public void run() throws Exception { */ public Single requestBatteryLevelUpdate(final boolean checkConnection){ final SingleEmitter[] observer = new SingleEmitter[1]; - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter subscriber) { - try { - txInterface.readValue(BleBattClient.this,BATTERY_SERVICE, BATTERY_LEVEL_CHARACTERISTIC); - observer[0] = subscriber; - observers.add(subscriber); - } catch (Throwable throwable) { - subscriber.tryOnError(throwable); - } - } - }).doFinally(new Action() { - @Override - public void run() { - observers.remove(observer[0]); + return Single.create((SingleOnSubscribe) subscriber -> { + try { + txInterface.readValue(BleBattClient.this,BATTERY_SERVICE, BATTERY_LEVEL_CHARACTERISTIC); + observer[0] = subscriber; + observers.add(subscriber); + } catch (Throwable throwable) { + subscriber.tryOnError(throwable); } - }); + }).doFinally(() -> observers.remove(observer[0])); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleDisClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleDisClient.java index a00d751b..d0244973 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleDisClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleDisClient.java @@ -9,18 +9,14 @@ import com.androidcommunications.polar.common.ble.AtomicSet; import com.androidcommunications.polar.common.ble.RxUtils; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.HashMap; -import java.util.Map; import java.util.UUID; -import io.reactivex.BackpressureStrategy; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; -import io.reactivex.annotations.NonNull; -import io.reactivex.functions.Action; +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; +import io.reactivex.rxjava3.core.FlowableOnSubscribe; public class BleDisClient extends BleGattBase { @@ -39,7 +35,7 @@ public class BleDisClient extends BleGattBase { // store in map private final HashMap disInformation = new HashMap<>(); - private final AtomicSet > > disObserverAtomicList = new AtomicSet<>(); + private final AtomicSet >> disObserverAtomicList = new AtomicSet<>(); public BleDisClient(BleGattTxInterface txInterface) { super(txInterface,DIS_SERVICE); @@ -69,14 +65,11 @@ public void processServiceData(final UUID characteristic, final byte[] data, int synchronized (disInformation) { disInformation.put(characteristic, new String(data, StandardCharsets.UTF_8)); } - RxUtils.emitNext(disObserverAtomicList, new RxUtils.Emitter>>() { - @Override - public void item(FlowableEmitter> object) { - object.onNext(new Pair<>(characteristic, new String(data, StandardCharsets.UTF_8))); - synchronized (disInformation) { - if (hasAllAvailableReadableCharacteristics(disInformation.keySet())) { - object.onComplete(); - } + RxUtils.emitNext(disObserverAtomicList, object -> { + object.onNext(new Pair<>(characteristic, new String(data, StandardCharsets.UTF_8))); + synchronized (disInformation) { + if (hasAllAvailableReadableCharacteristics(disInformation.keySet())) { + object.onComplete(); } } }); @@ -104,30 +97,22 @@ public String toString() { */ public Flowable > observeDisInfo(final boolean checkConnection) { final FlowableEmitter>[] observer = new FlowableEmitter[1]; - return Flowable.create(new FlowableOnSubscribe>() { - @Override - public void subscribe(@NonNull FlowableEmitter> subscriber) { - if ( !checkConnection || BleDisClient.this.txInterface.isConnected() ) { - observer[0] = subscriber; - disObserverAtomicList.add(subscriber); - synchronized (disInformation) { - for(UUID e : disInformation.keySet()){ - subscriber.onNext(new Pair(e,disInformation.get(e))); - } - if (hasAllAvailableReadableCharacteristics(disInformation.keySet())) { - subscriber.onComplete(); - } + return Flowable.create((FlowableOnSubscribe>) subscriber -> { + if ( !checkConnection || BleDisClient.this.txInterface.isConnected() ) { + observer[0] = subscriber; + disObserverAtomicList.add(subscriber); + synchronized (disInformation) { + for(UUID e : disInformation.keySet()){ + subscriber.onNext(new Pair<>(e, disInformation.get(e))); + } + if (hasAllAvailableReadableCharacteristics(disInformation.keySet())) { + subscriber.onComplete(); } - } else if(!subscriber.isCancelled()) { - subscriber.tryOnError(new BleDisconnected()); } + } else if(!subscriber.isCancelled()) { + subscriber.tryOnError(new BleDisconnected()); } - },BackpressureStrategy.BUFFER).doFinally(new Action() { - @Override - public void run() { - disObserverAtomicList.remove(observer[0]); - } - }); + }, BackpressureStrategy.BUFFER).doFinally(() -> disObserverAtomicList.remove(observer[0])); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleGapClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleGapClient.java index ae5b070a..b067df17 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleGapClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleGapClient.java @@ -9,11 +9,10 @@ import java.util.HashMap; import java.util.UUID; -import io.reactivex.BackpressureStrategy; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; -import io.reactivex.functions.Action; +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; +import io.reactivex.rxjava3.core.FlowableOnSubscribe; public class BleGapClient extends BleGattBase { @@ -46,17 +45,14 @@ public void processServiceData(UUID characteristic, byte[] data, int status, boo gapInformation.put(characteristic, new String(data)); } - RxUtils.emitNext(gapObserverAtomicList, new RxUtils.Emitter>>() { - @Override - public void item(FlowableEmitter> object) { - HashMap list; - synchronized (gapInformation) { - list = new HashMap<>(gapInformation); - } - object.onNext(list); - if (hasAllAvailableReadableCharacteristics(list.keySet())) { - object.onComplete(); - } + RxUtils.emitNext(gapObserverAtomicList, object -> { + HashMap list; + synchronized (gapInformation) { + list = new HashMap<>(gapInformation); + } + object.onNext(list); + if (hasAllAvailableReadableCharacteristics(list.keySet())) { + object.onComplete(); } }); } @@ -81,31 +77,23 @@ public String toString() { */ public Flowable > observeGapInfo(final boolean checkConnection) { final FlowableEmitter>[] observer = new FlowableEmitter[]{null}; - return Flowable.create(new FlowableOnSubscribe>() { - @Override - public void subscribe(FlowableEmitter> subscriber) throws Exception { - if( !checkConnection || BleGapClient.this.txInterface.isConnected() ) { - observer[0] = subscriber; - gapObserverAtomicList.add(subscriber); - HashMap list; - synchronized (gapInformation) { - list = new HashMap<>(gapInformation); - } - if (list.size() != 0) { - subscriber.onNext(list); - } - if (hasAllAvailableReadableCharacteristics(list.keySet())) { - subscriber.onComplete(); - } - }else if(!subscriber.isCancelled()){ - subscriber.tryOnError(new BleDisconnected()); + return Flowable.create((FlowableOnSubscribe>) subscriber -> { + if( !checkConnection || BleGapClient.this.txInterface.isConnected() ) { + observer[0] = subscriber; + gapObserverAtomicList.add(subscriber); + HashMap list; + synchronized (gapInformation) { + list = new HashMap<>(gapInformation); } + if (list.size() != 0) { + subscriber.onNext(list); + } + if (hasAllAvailableReadableCharacteristics(list.keySet())) { + subscriber.onComplete(); + } + }else if(!subscriber.isCancelled()){ + subscriber.tryOnError(new BleDisconnected()); } - }, BackpressureStrategy.BUFFER).doFinally(new Action() { - @Override - public void run() { - gapObserverAtomicList.remove(observer[0]); - } - }); + }, BackpressureStrategy.BUFFER).doFinally(() -> gapObserverAtomicList.remove(observer[0])); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleH7SettingsClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleH7SettingsClient.java index 980a1181..d944b626 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleH7SettingsClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleH7SettingsClient.java @@ -6,17 +6,15 @@ import com.androidcommunications.polar.api.ble.model.gatt.BleGattBase; import com.androidcommunications.polar.api.ble.model.gatt.BleGattTxInterface; -import java.util.Arrays; import java.util.Collections; import java.util.UUID; -import java.util.concurrent.Callable; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.schedulers.Schedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.core.SingleEmitter; +import io.reactivex.rxjava3.core.SingleOnSubscribe; +import io.reactivex.rxjava3.schedulers.Schedulers; public class BleH7SettingsClient extends BleGattBase { public static final UUID H7_SETTINGS_CHARACTERISTIC = UUID.fromString("6217FF4A-B07D-5DEB-261E-2586752D942E"); @@ -142,74 +140,71 @@ private byte[] readSettingsValue() throws Exception{ * @return Single stream */ public Single sendSettingsCommand(final H7SettingsMessage command, final byte parameter) { - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter emitter) { - final boolean has = getAvailableCharacteristics().contains(H7_SETTINGS_CHARACTERISTIC); - try { - synchronized (mutex) { - if (txInterface.isConnected()) { - if (has) { - try { - h7InputQueue.clear(); - int khzValue; - int broadcastValue; - byte[] packet = BleH7SettingsClient.this.readSettingsValue(); - khzValue = (packet[0] & 0x02) >> 1; - broadcastValue = packet[0] & 0x01; - - switch (command) { - case H7_CONFIGURE_5KHZ: - case H7_CONFIGURE_BROADCAST: { - byte[] values = new byte[1]; - if (command == H7SettingsMessage.H7_CONFIGURE_BROADCAST) { - values[0] = (byte) ((khzValue << 1) | parameter); + return Single.create((SingleOnSubscribe) emitter -> { + final boolean has = getAvailableCharacteristics().contains(H7_SETTINGS_CHARACTERISTIC); + try { + synchronized (mutex) { + if (txInterface.isConnected()) { + if (has) { + try { + h7InputQueue.clear(); + int khzValue; + int broadcastValue; + byte[] packet = BleH7SettingsClient.this.readSettingsValue(); + khzValue = (packet[0] & 0x02) >> 1; + broadcastValue = packet[0] & 0x01; + + switch (command) { + case H7_CONFIGURE_5KHZ: + case H7_CONFIGURE_BROADCAST: { + byte[] values = new byte[1]; + if (command == H7SettingsMessage.H7_CONFIGURE_BROADCAST) { + values[0] = (byte) ((khzValue << 1) | parameter); + } else { + values[0] = (byte) ((parameter << 1) | broadcastValue); + } + txInterface.transmitMessages(BleH7SettingsClient.this, + H7_SETTINGS_SERVICE, + H7_SETTINGS_CHARACTERISTIC, + Collections.singletonList(values), true); + Integer error = h7WrittenQueue.poll(30, TimeUnit.SECONDS); + if (error != null) { + if (error == 0) { + emitter.onSuccess(new H7SettingsResponse(values)); + return; } else { - values[0] = (byte) ((parameter << 1) | broadcastValue); + throw new Exception("Failed to write settings: " + error); } - txInterface.transmitMessages(BleH7SettingsClient.this, - H7_SETTINGS_SERVICE, - H7_SETTINGS_CHARACTERISTIC, - Collections.singletonList(values), true); - Integer error = h7WrittenQueue.poll(30, TimeUnit.SECONDS); - if (error != null) { - if (error == 0) { - emitter.onSuccess(new H7SettingsResponse(values)); - return; - } else { - throw new Exception("Failed to write settings: " + error); - } + } else { + if (!txInterface.isConnected()) { + throw new BleDisconnected(); } else { - if (!txInterface.isConnected()) { - throw new BleDisconnected(); - } else { - throw new Exception("Failed to write packet in timeline"); - } + throw new Exception("Failed to write packet in timeline"); } } - case H7_REQUEST_CURRENT_SETTINGS: { - emitter.onSuccess(new H7SettingsResponse(khzValue, broadcastValue)); - return; - } - default: - throw new BleNotSupported("Unknown h7 command"); } - } catch (Exception e) { - if (!emitter.isDisposed()) { - throw e; + case H7_REQUEST_CURRENT_SETTINGS: { + emitter.onSuccess(new H7SettingsResponse(khzValue, broadcastValue)); + return; } + default: + throw new BleNotSupported("Unknown h7 command"); + } + } catch (Exception e) { + if (!emitter.isDisposed()) { + throw e; } - } else { - throw new BleCharacteristicNotFound(); } } else { - throw new BleDisconnected(); + throw new BleCharacteristicNotFound(); } + } else { + throw new BleDisconnected(); } - } catch (Exception e){ - if(!emitter.isDisposed()){ - emitter.tryOnError(e); - } + } + } catch (Exception e){ + if(!emitter.isDisposed()){ + emitter.tryOnError(e); } } }).subscribeOn(Schedulers.io()); diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleHrClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleHrClient.java index 796735f6..d679602e 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleHrClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleHrClient.java @@ -8,9 +8,9 @@ import java.util.ArrayList; import java.util.UUID; -import io.reactivex.Completable; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; public class BleHrClient extends BleGattBase { @@ -90,12 +90,7 @@ public void processServiceData(UUID characteristic, byte[] data, int status, boo final int finalCumulative_rr = cumulative_rr; final int finalEnergy = energy; - RxUtils.emitNext(hrObserverAtomicList, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new HrNotificationData(hrValue, sensorContact, finalEnergy, rrs, contactSupported, finalCumulative_rr, rrPresent == 1)); - } - }); + RxUtils.emitNext(hrObserverAtomicList, object -> object.onNext(new HrNotificationData(hrValue, sensorContact, finalEnergy, rrs, contactSupported, finalCumulative_rr, rrPresent == 1))); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePMDClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePMDClient.java index a3d7b071..3bab33eb 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePMDClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePMDClient.java @@ -17,7 +17,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -28,16 +27,14 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.Completable; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.SingleSource; -import io.reactivex.functions.Consumer; -import io.reactivex.functions.Function; -import io.reactivex.schedulers.Schedulers; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.core.SingleEmitter; +import io.reactivex.rxjava3.core.SingleOnSubscribe; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.schedulers.Schedulers; public class BlePMDClient extends BleGattBase { @@ -578,12 +575,7 @@ public void processServiceData(UUID characteristic, final byte[] data, int statu switch (type) { case ECG: if(frameType <= 2) { - RxUtils.emitNext(ecgObservers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new EcgData(frameType, content, timeStamp)); - } - }); + RxUtils.emitNext(ecgObservers, object -> object.onNext(new EcgData(frameType, content, timeStamp))); } else { BleLogger.w(TAG,"Unknown ECG frame type received"); } @@ -592,58 +584,32 @@ public void item(FlowableEmitter object) { switch (PpgData.PpgFrameType.fromId(frameType)) { case PPG1_TYPE: case PPG0_TYPE: { - RxUtils.emitNext(ppgObservers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new PpgData(content, timeStamp, frameType)); - } - }); + RxUtils.emitNext(ppgObservers, object -> object.onNext(new PpgData(content, timeStamp, frameType))); break; } case AFE4410: { - RxUtils.emitNext(autoGainAFE4410Observers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new AutoGainAFE4410(content, timeStamp)); - } - }); + RxUtils.emitNext(autoGainAFE4410Observers, object -> object.onNext(new AutoGainAFE4410(content, timeStamp))); break; } case AFE4404: { - RxUtils.emitNext(autoGainAFE4404Observers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new AutoGainAFE4404(content, timeStamp)); - } - }); + RxUtils.emitNext(autoGainAFE4404Observers, object -> object.onNext(new AutoGainAFE4404(content, timeStamp))); break; } case ADPD4000: { - RxUtils.emitNext(autoGainADPD4000Observers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new AutoGainADPD4000(content, timeStamp)); - } - }); + RxUtils.emitNext(autoGainADPD4000Observers, object -> object.onNext(new AutoGainADPD4000(content, timeStamp))); break; } case AFE_OPERATION_MODE: { - RxUtils.emitNext(afeOperationModeObservers, new RxUtils.Emitter>>() { - @Override - public void item(FlowableEmitter> object) { - Long value = BleUtils.convertArrayToUnsignedLong(content, 0, content.length); - object.onNext(new Pair(timeStamp,value)); - } + RxUtils.emitNext(afeOperationModeObservers, object -> { + Long value = BleUtils.convertArrayToUnsignedLong(content, 0, content.length); + object.onNext(new Pair<>(timeStamp, value)); }); break; } case SPORT_ID: { - RxUtils.emitNext(sportIdObservers, new RxUtils.Emitter>>() { - @Override - public void item(FlowableEmitter> object) { - final long sportId = BleUtils.convertArrayToUnsignedLong(content, 0,8); - object.onNext(new Pair(timeStamp,sportId)); - } + RxUtils.emitNext(sportIdObservers, object -> { + final long sportId = BleUtils.convertArrayToUnsignedLong(content, 0,8); + object.onNext(new Pair<>(timeStamp, sportId)); }); break; } @@ -654,24 +620,14 @@ public void item(FlowableEmitter> object) { break; case ACC: if(frameType <= 2) { - RxUtils.emitNext(accObservers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new AccData(frameType, content, timeStamp)); - } - }); + RxUtils.emitNext(accObservers, object -> object.onNext(new AccData(frameType, content, timeStamp))); } else { BleLogger.w(TAG,"Unknown ACC frame type received"); } break; case PPI: if( frameType == 0 ) { - RxUtils.emitNext(ppiObservers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new PpiData(content, timeStamp)); - } - }); + RxUtils.emitNext(ppiObservers, object -> object.onNext(new PpiData(content, timeStamp))); } else { BleLogger.w(TAG,"Unknown PPI frame type received"); } @@ -679,12 +635,7 @@ public void item(FlowableEmitter object) { case BIOZ: if( frameType == 0 || frameType == 1 ){ - RxUtils.emitNext(biozObservers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new BiozData(content,timeStamp, frameType)); - } - }); + RxUtils.emitNext(biozObservers, object -> object.onNext(new BiozData(content,timeStamp, frameType))); } else { BleLogger.w(TAG,"Unknown BIOZ frame type received"); } @@ -739,27 +690,24 @@ private Single sendControlPointCommand(final PmdControl } private Single sendControlPointCommand(final PmdControlPointCommand command, final byte[] params){ - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter subscriber) throws Exception { - synchronized (controlPointMutex){ - try { - if (pmdCpEnabled.get() == ATT_SUCCESS && pmdDataEnabled.get() == ATT_SUCCESS) { - ByteBuffer bb = ByteBuffer.allocate(1 + params.length); - bb.put(new byte[]{(byte) command.getNumVal()}); - bb.put(params); - PmdControlPointResponse response = sendPmdCommand(bb.array()); - if (response.status == PmdControlPointResponse.PmdControlPointResponseCode.SUCCESS) { - subscriber.onSuccess(response); - return; - } - throw new Exception("Pmd cp failed: " + response.status.numVal); - } - throw new BleCharacteristicNotificationNotEnabled(); - } catch (Throwable throwable){ - if(!subscriber.isDisposed()) { - subscriber.tryOnError(throwable); + return Single.create((SingleOnSubscribe) subscriber -> { + synchronized (controlPointMutex){ + try { + if (pmdCpEnabled.get() == ATT_SUCCESS && pmdDataEnabled.get() == ATT_SUCCESS) { + ByteBuffer bb = ByteBuffer.allocate(1 + params.length); + bb.put(new byte[]{(byte) command.getNumVal()}); + bb.put(params); + PmdControlPointResponse response = sendPmdCommand(bb.array()); + if (response.status == PmdControlPointResponse.PmdControlPointResponseCode.SUCCESS) { + subscriber.onSuccess(response); + return; } + throw new Exception("Pmd cp failed: " + response.status.numVal); + } + throw new BleCharacteristicNotificationNotEnabled(); + } catch (Throwable throwable){ + if(!subscriber.isDisposed()) { + subscriber.tryOnError(throwable); } } } @@ -783,29 +731,26 @@ public PmdSetting apply(PmdControlPointResponse pmdControlPointResponse) throws * @return Single stream */ public Single readFeature(final boolean checkConnection) { - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter emitter) throws Exception { - try { - if(!checkConnection || txInterface.isConnected()) { - synchronized (mutexFeature) { - if (pmdFeatureData == null) { - mutexFeature.wait(); - } - if (pmdFeatureData != null) { - emitter.onSuccess(new PmdFeature(pmdFeatureData)); - return; - } else if (!txInterface.isConnected()) { - throw new BleDisconnected(); - } - throw new Exception("Undefined device error"); + return Single.create((SingleOnSubscribe) emitter -> { + try { + if(!checkConnection || txInterface.isConnected()) { + synchronized (mutexFeature) { + if (pmdFeatureData == null) { + mutexFeature.wait(); } + if (pmdFeatureData != null) { + emitter.onSuccess(new PmdFeature(pmdFeatureData)); + return; + } else if (!txInterface.isConnected()) { + throw new BleDisconnected(); + } + throw new Exception("Undefined device error"); } - throw new BleDisconnected(); - } catch (Exception ex){ - if(!emitter.isDisposed()){ - emitter.tryOnError(ex); - } + } + throw new BleDisconnected(); + } catch (Exception ex){ + if(!emitter.isDisposed()){ + emitter.tryOnError(ex); } } }).subscribeOn(Schedulers.io()); @@ -957,4 +902,4 @@ public PmdFeature getPmdFeatureData() { } } } -} +} \ No newline at end of file diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePfcClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePfcClient.java index 4e10f4c9..4a0d3c72 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePfcClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePfcClient.java @@ -2,7 +2,6 @@ import android.util.Pair; -import com.androidcommunications.polar.api.ble.BleLogger; import com.androidcommunications.polar.api.ble.exceptions.BleAttributeError; import com.androidcommunications.polar.api.ble.exceptions.BleCharacteristicNotificationNotEnabled; import com.androidcommunications.polar.api.ble.exceptions.BleDisconnected; @@ -12,20 +11,17 @@ import com.androidcommunications.polar.api.ble.model.gatt.BleGattTxInterface; import java.nio.ByteBuffer; -import java.util.Arrays; import java.util.Collections; import java.util.UUID; -import java.util.concurrent.Callable; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.Completable; -import io.reactivex.Scheduler; -import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.schedulers.Schedulers; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Scheduler; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.core.SingleOnSubscribe; +import io.reactivex.rxjava3.schedulers.Schedulers; public class BlePfcClient extends BleGattBase { @@ -231,47 +227,44 @@ public Completable clientReady(boolean checkConnection) { * @return Observable stream, @see Rx Observable */ public Single sendControlPointCommand(final PfcMessage command, final byte[] params) { - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter emitter) throws Exception { - // force pfc operation to be 'atomic' - synchronized (pfcMutex) { - if (pfcCpEnabled.get() == ATT_SUCCESS) { - try { - pfcCpInputQueue.clear(); - switch (command) { - case PFC_CONFIGURE_ANT_PLUS_SETTING: - case PFC_CONFIGURE_MULTI_CONNECTION_SETTING: - case PFC_CONFIGURE_BLE_MODE: - case PFC_CONFIGURE_WHISPER_MODE: - case PFC_CONFIGURE_BROADCAST: - case PFC_CONFIGURE_5KHZ: { - ByteBuffer bb = ByteBuffer.allocate(1 + params.length); - bb.put(new byte[]{(byte) command.getNumVal()}); - bb.put(params); - emitter.onSuccess(sendPfcCommandAndProcessResponse(bb.array())); - return; - } - case PFC_REQUEST_MULTI_CONNECTION_SETTING: - case PFC_REQUEST_ANT_PLUS_SETTING: - case PFC_REQUEST_WHISPER_MODE: - case PFC_REQUEST_BROADCAST_SETTING: - case PFC_REQUEST_5KHZ_SETTING: { - byte[] packet = new byte[]{(byte) command.getNumVal()}; - emitter.onSuccess(sendPfcCommandAndProcessResponse(packet)); - return; - } - default: - throw new BleNotSupported("Unknown pfc command aquired"); + return Single.create((SingleOnSubscribe) emitter -> { + // force pfc operation to be 'atomic' + synchronized (pfcMutex) { + if (pfcCpEnabled.get() == ATT_SUCCESS) { + try { + pfcCpInputQueue.clear(); + switch (command) { + case PFC_CONFIGURE_ANT_PLUS_SETTING: + case PFC_CONFIGURE_MULTI_CONNECTION_SETTING: + case PFC_CONFIGURE_BLE_MODE: + case PFC_CONFIGURE_WHISPER_MODE: + case PFC_CONFIGURE_BROADCAST: + case PFC_CONFIGURE_5KHZ: { + ByteBuffer bb = ByteBuffer.allocate(1 + params.length); + bb.put(new byte[]{(byte) command.getNumVal()}); + bb.put(params); + emitter.onSuccess(sendPfcCommandAndProcessResponse(bb.array())); + return; } - } catch (Exception ex){ - if(!emitter.isDisposed()){ - emitter.tryOnError(ex); + case PFC_REQUEST_MULTI_CONNECTION_SETTING: + case PFC_REQUEST_ANT_PLUS_SETTING: + case PFC_REQUEST_WHISPER_MODE: + case PFC_REQUEST_BROADCAST_SETTING: + case PFC_REQUEST_5KHZ_SETTING: { + byte[] packet = new byte[]{(byte) command.getNumVal()}; + emitter.onSuccess(sendPfcCommandAndProcessResponse(packet)); + return; } + default: + throw new BleNotSupported("Unknown pfc command aquired"); + } + } catch (Exception ex){ + if(!emitter.isDisposed()){ + emitter.tryOnError(ex); } - } else if(!emitter.isDisposed()) { - emitter.tryOnError(new BleCharacteristicNotificationNotEnabled("PFC control point not enabled")); } + } else if(!emitter.isDisposed()) { + emitter.tryOnError(new BleCharacteristicNotificationNotEnabled("PFC control point not enabled")); } } }).subscribeOn(scheduler); @@ -285,26 +278,23 @@ public void subscribe(SingleEmitter emitter) throws Exception { * - onError, @see errors package */ public Single readFeature(){ - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter emitter) { - try { - synchronized (mutexFeature) { - if (pfcFeature == null) { - mutexFeature.wait(); - } - if (pfcFeature != null) { - emitter.onSuccess(new PfcFeature(pfcFeature)); - return; - } else if (!txInterface.isConnected()) { - throw new BleDisconnected(); - } - throw new Exception("Undefined device error"); + return Single.create((SingleOnSubscribe) emitter -> { + try { + synchronized (mutexFeature) { + if (pfcFeature == null) { + mutexFeature.wait(); } - } catch (Exception ex){ - if(!emitter.isDisposed()){ - emitter.tryOnError(ex); + if (pfcFeature != null) { + emitter.onSuccess(new PfcFeature(pfcFeature)); + return; + } else if (!txInterface.isConnected()) { + throw new BleDisconnected(); } + throw new Exception("Undefined device error"); + } + } catch (Exception ex){ + if(!emitter.isDisposed()){ + emitter.tryOnError(ex); } } }).subscribeOn(Schedulers.io()); diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePsdClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePsdClient.java index 91888f96..24bc53b0 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePsdClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BlePsdClient.java @@ -12,24 +12,22 @@ import com.androidcommunications.polar.common.ble.AtomicSet; import com.androidcommunications.polar.common.ble.RxUtils; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Set; import java.util.UUID; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.Completable; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.Scheduler; -import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.schedulers.Schedulers; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; +import io.reactivex.rxjava3.core.Scheduler; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.core.SingleEmitter; +import io.reactivex.rxjava3.core.SingleOnSubscribe; +import io.reactivex.rxjava3.schedulers.Schedulers; public class BlePsdClient extends BleGattBase { @@ -248,12 +246,7 @@ public void processServiceData(UUID characteristic, final byte[] data, int statu if(characteristic.equals(PSD_PP)){ List list = splitPP(data); for(final byte[] packet : list){ - RxUtils.emitNext(ppObservers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new PPData(packet)); - } - }); + RxUtils.emitNext(ppObservers, object -> object.onNext(new PPData(packet))); } } } @@ -307,57 +300,51 @@ public Completable clientReady(boolean checkConnection) { * @return Observable stream, @see Rx Observer */ public Single sendControlPointCommand(final PsdMessage command, final byte[] params) { - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter emitter) throws Exception { - synchronized (psdMutex) { - if (psdCpEnabled.get() == ATT_SUCCESS) { - try { - psdCpInputQueue.clear(); - switch (command) { - case PSD_STOP_OHR_PP_STREAM: - case PSD_START_OHR_PP_STREAM: { - byte[] packet = new byte[]{(byte) command.getNumVal()}; - emitter.onSuccess(sendPsdCommandAndProcessResponse(packet)); - return; - } - default: - throw new BleNotSupported("Unknown psd command aquired"); - } - } catch (Exception ex){ - if(!emitter.isDisposed()){ - emitter.tryOnError(ex); + return Single.create((SingleOnSubscribe) emitter -> { + synchronized (psdMutex) { + if (psdCpEnabled.get() == ATT_SUCCESS) { + try { + psdCpInputQueue.clear(); + switch (command) { + case PSD_STOP_OHR_PP_STREAM: + case PSD_START_OHR_PP_STREAM: { + byte[] packet = new byte[]{(byte) command.getNumVal()}; + emitter.onSuccess(sendPsdCommandAndProcessResponse(packet)); + return; } + default: + throw new BleNotSupported("Unknown psd command aquired"); + } + } catch (Exception ex){ + if(!emitter.isDisposed()){ + emitter.tryOnError(ex); } - } else if (!emitter.isDisposed()){ - emitter.tryOnError(new BleCharacteristicNotificationNotEnabled("PSD control point not enabled")); } + } else if (!emitter.isDisposed()){ + emitter.tryOnError(new BleCharacteristicNotificationNotEnabled("PSD control point not enabled")); } } }).subscribeOn(scheduler); } public Single readFeature(){ - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter emitter) { - try { - synchronized (mutexFeature) { - if (psdFeature == null) { - mutexFeature.wait(); - } - if (psdFeature != null) { - emitter.onSuccess(new PsdFeature(psdFeature)); - return; - } else if (!txInterface.isConnected()) { - throw new BleDisconnected(); - } - throw new Exception("Undefined device error"); + return Single.create((SingleOnSubscribe) emitter -> { + try { + synchronized (mutexFeature) { + if (psdFeature == null) { + mutexFeature.wait(); } - } catch (Exception ex){ - if(!emitter.isDisposed()){ - emitter.tryOnError(ex); + if (psdFeature != null) { + emitter.onSuccess(new PsdFeature(psdFeature)); + return; + } else if (!txInterface.isConnected()) { + throw new BleDisconnected(); } + throw new Exception("Undefined device error"); + } + } catch (Exception ex){ + if(!emitter.isDisposed()){ + emitter.tryOnError(ex); } } }).subscribeOn(Schedulers.io()); diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleRscClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleRscClient.java index 2567c608..89ffb1f7 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleRscClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/BleRscClient.java @@ -8,9 +8,9 @@ import java.util.UUID; -import io.reactivex.Completable; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; public class BleRscClient extends BleGattBase { @@ -82,12 +82,7 @@ public void processServiceData(UUID characteristic, byte[] data, int status, boo final long finalStrideLength = StrideLength; final long finalTotalDistance = TotalDistance; - RxUtils.emitNext(observers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(new RscNotificationData(StrideLenPresent,TotalDistancePresent,Running, Speed, Cadence, finalStrideLength, finalTotalDistance)); - } - }); + RxUtils.emitNext(observers, object -> object.onNext(new RscNotificationData(StrideLenPresent,TotalDistancePresent,Running, Speed, Cadence, finalStrideLength, finalTotalDistance))); }else if(characteristic.equals(RSC_FEATURE)){ long feature = data[0] | data[1] << 8; BleLogger.d(TAG, "RSC Feature Characteristic read: " + feature); diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/psftp/BlePsFtpClient.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/psftp/BlePsFtpClient.java index e43fe52c..a057f5f0 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/psftp/BlePsFtpClient.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/psftp/BlePsFtpClient.java @@ -9,7 +9,6 @@ import com.androidcommunications.polar.api.ble.model.gatt.BleGattBase; import com.androidcommunications.polar.api.ble.model.gatt.BleGattTxInterface; -import org.reactivestreams.Subscription; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Collections; @@ -20,23 +19,15 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.BackpressureOverflowStrategy; -import io.reactivex.BackpressureStrategy; -import io.reactivex.Completable; -import io.reactivex.CompletableEmitter; -import io.reactivex.CompletableOnSubscribe; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; -import io.reactivex.Scheduler; -import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.annotations.NonNull; -import io.reactivex.disposables.Disposable; -import io.reactivex.functions.Action; -import io.reactivex.functions.Consumer; -import io.reactivex.schedulers.Schedulers; +import io.reactivex.rxjava3.core.BackpressureOverflowStrategy; +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableOnSubscribe; +import io.reactivex.rxjava3.core.Scheduler; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.core.SingleOnSubscribe; +import io.reactivex.rxjava3.schedulers.Schedulers; /** * Polar simple file transfer client declaration. @@ -46,8 +37,8 @@ public class BlePsFtpClient extends BleGattBase { private static final String TAG = BlePsFtpClient.class.getSimpleName(); private AtomicInteger pftpMtuEnabled; private AtomicInteger pftpD2HNotificationEnabled; - private final LinkedBlockingQueue > mtuInputQueue = new LinkedBlockingQueue<>(); - private final LinkedBlockingQueue > notificationInputQueue = new LinkedBlockingQueue<>(); + private final LinkedBlockingQueue> mtuInputQueue = new LinkedBlockingQueue<>(); + private final LinkedBlockingQueue> notificationInputQueue = new LinkedBlockingQueue<>(); private final AtomicInteger packetsWritten = new AtomicInteger(0); private final AtomicInteger packetsWrittenWithResponse = new AtomicInteger(0); private final AtomicBoolean mtuWaiting = new AtomicBoolean(false); @@ -78,9 +69,10 @@ public BlePsFtpClient(BleGattTxInterface txInterface) { /** * set amount of packets which to be written before att level response + * * @param count number of packets */ - public void setPacketsCount(int count){ + public void setPacketsCount(int count) { packetsCount.set(count); } @@ -89,27 +81,27 @@ public void reset() { super.reset(); currentOperationWrite.set(false); mtuInputQueue.clear(); - synchronized (mtuInputQueue){ + synchronized (mtuInputQueue) { mtuInputQueue.notifyAll(); } packetsWritten.set(0); - synchronized (packetsWritten){ + synchronized (packetsWritten) { packetsWritten.notifyAll(); } packetsWrittenWithResponse.set(0); - synchronized (packetsWrittenWithResponse){ + synchronized (packetsWrittenWithResponse) { packetsWrittenWithResponse.notifyAll(); } notificationInputQueue.clear(); - synchronized (notificationInputQueue){ + synchronized (notificationInputQueue) { notificationInputQueue.notifyAll(); } notificationPacketsWritten.set(0); - synchronized (notificationPacketsWritten){ + synchronized (notificationPacketsWritten) { notificationPacketsWritten.notifyAll(); } @@ -119,15 +111,15 @@ public void reset() { @Override public void processServiceData(UUID characteristic, byte[] data, int status, boolean notifying) { - if(data.length!=0) { + if (data.length != 0) { if (characteristic.equals(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC)) { synchronized (mtuInputQueue) { mtuInputQueue.add(new Pair<>(data, status)); mtuInputQueue.notifyAll(); } - if(currentOperationWrite.get() && mtuWaiting.get() && data.length == 3){ + if (currentOperationWrite.get() && mtuWaiting.get() && data.length == 3) { // special case stream cancelation has been received before att response - synchronized (packetsWritten){ + synchronized (packetsWritten) { packetsWritten.incrementAndGet(); packetsWritten.notifyAll(); } @@ -138,22 +130,22 @@ public void processServiceData(UUID characteristic, byte[] data, int status, boo notificationInputQueue.notifyAll(); } } - }else{ - BleLogger.e(TAG,"Received 0 length packet"); + } else { + BleLogger.e(TAG, "Received 0 length packet"); } } @Override public void processServiceDataWritten(UUID characteristic, int status) { - if(status==0) { - if (characteristic.equals(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC) ) { - synchronized (packetsWritten){ + if (status == 0) { + if (characteristic.equals(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC)) { + synchronized (packetsWritten) { packetsWritten.incrementAndGet(); packetsWritten.notifyAll(); } - }else if(characteristic.equals(BlePsFtpUtils.RFC77_PFTP_H2D_CHARACTERISTIC)){ + } else if (characteristic.equals(BlePsFtpUtils.RFC77_PFTP_H2D_CHARACTERISTIC)) { //BleLogger.d(TAG, "NOTIFICATION characteristic air packet written"); - synchronized (notificationPacketsWritten){ + synchronized (notificationPacketsWritten) { notificationPacketsWritten.incrementAndGet(); notificationPacketsWritten.notifyAll(); } @@ -161,15 +153,15 @@ public void processServiceDataWritten(UUID characteristic, int status) { } else { // print informal info, NOTE current implementation will result to timeout, which is ok as both ends will // reset them selfs with timeout - BleLogger.e(TAG,"Failed to write chr UUID: " + characteristic.toString() + " status: " + status); + BleLogger.e(TAG, "Failed to write chr UUID: " + characteristic.toString() + " status: " + status); } } @Override public void processServiceDataWrittenWithResponse(UUID characteristic, int status) { - if(status == 0){ - if (characteristic.equals(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC) ) { - synchronized (packetsWrittenWithResponse){ + if (status == 0) { + if (characteristic.equals(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC)) { + synchronized (packetsWrittenWithResponse) { packetsWrittenWithResponse.incrementAndGet(); packetsWrittenWithResponse.notifyAll(); } @@ -183,14 +175,14 @@ public String toString() { return "RFC77 Service"; } - private void resetMtuPipe(){ + private void resetMtuPipe() { BleLogger.d(TAG, "mtu reseted"); mtuInputQueue.clear(); packetsWritten.set(0); mtuWaiting.set(false); } - private void resetNotificationPipe(){ + private void resetNotificationPipe() { notificationPacketsWritten.set(0); notificationWaiting.set(false); } @@ -201,65 +193,52 @@ public Completable clientReady(boolean checkConnection) { } public Single request(final byte[] header) { - return request(header, Schedulers.newThread(), true); + return request(header, Schedulers.newThread()); } /** * Sends a request to device (get, remove or put(create dir, without data)), atomic operation
- * @param header protobuf pftp operation bytes, GET , REMOVE or PUT(create dir etc... without data)
+ * + * @param header protobuf pftp operation bytes, GET , REMOVE or PUT(create dir etc... without data)
* @param scheduler scheduler where to start operation execution
- * @param bypassCache false = use cache if possible, true = bypass cache
* @return Observable Produces: onNext, only once when file has been successfully read
- * onCompleted, called after onNext
- * onError, if any error happens during file operation, @see BlePsFtpUtils for possible exceptions
+ * onCompleted, called after onNext
+ * onError, if any error happens during file operation, @see BlePsFtpUtils for possible exceptions
*/ - public Single request(final byte[] header, Scheduler scheduler, @Deprecated final boolean bypassCache) { - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter emitter) { - // block, until previous operation has completed - try { - synchronized (pftpOperationMutex) { - if (pftpMtuEnabled.get() == ATT_SUCCESS) { - resetMtuPipe(); - // transmit header first - ByteArrayInputStream totalStream = BlePsFtpUtils.makeCompleteMessageStream(new ByteArrayInputStream(header), null, BlePsFtpUtils.MessageType.REQUEST, 0); - BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); - List requs = BlePsFtpUtils.buildRfc76MessageFrameAll(totalStream, mtuSize.get(), sequenceNumber); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - // - try { - txInterface.transmitMessages(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, requs, false); - waitPacketsWritten(packetsWritten, mtuWaiting, requs.size()); - requs.clear(); - // start waiting for packets - readResponse(outputStream); - emitter.onSuccess(outputStream); - } catch (InterruptedException ex) { - // canceled, note make improvement, only wait amount of packets transmitted - handleMtuInterrupted(true, requs.size()); - } - } else { - throw new BleCharacteristicNotificationNotEnabled("PS-FTP MTU not enabled"); + public Single request(final byte[] header, Scheduler scheduler) { + return Single.create((SingleOnSubscribe) emitter -> { + // block, until previous operation has completed + try { + synchronized (pftpOperationMutex) { + if (pftpMtuEnabled.get() == ATT_SUCCESS) { + resetMtuPipe(); + // transmit header first + ByteArrayInputStream totalStream = BlePsFtpUtils.makeCompleteMessageStream(new ByteArrayInputStream(header), null, BlePsFtpUtils.MessageType.REQUEST, 0); + BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); + List requs = BlePsFtpUtils.buildRfc76MessageFrameAll(totalStream, mtuSize.get(), sequenceNumber); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // + try { + txInterface.transmitMessages(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, requs, false); + waitPacketsWritten(packetsWritten, mtuWaiting, requs.size()); + requs.clear(); + // start waiting for packets + readResponse(outputStream); + emitter.onSuccess(outputStream); + } catch (InterruptedException ex) { + // canceled, note make improvement, only wait amount of packets transmitted + handleMtuInterrupted(true, requs.size()); } - } - } catch (Exception ex) { - if (!emitter.isDisposed()) { - emitter.tryOnError(ex); + } else { + throw new BleCharacteristicNotificationNotEnabled("PS-FTP MTU not enabled"); } } + } catch (Exception ex) { + if (!emitter.isDisposed()) { + emitter.tryOnError(ex); + } } - }).doOnSubscribe(new Consumer() { - @Override - public void accept(Disposable disposable) { - txInterface.gattClientRequestStopScanning(); - } - }).doFinally(new Action() { - @Override - public void run() { - txInterface.gattClientResumeScanning(); - } - }).subscribeOn(scheduler); + }).doOnSubscribe(disposable -> txInterface.gattClientRequestStopScanning()).doFinally(() -> txInterface.gattClientResumeScanning()).subscribeOn(scheduler); } private void waitPacketsWritten(final AtomicInteger written, AtomicBoolean waiting, int count) throws InterruptedException, BleDisconnected, BlePsFtpUtils.PftpOperationTimeout { @@ -290,131 +269,121 @@ private void waitPacketsWritten(final AtomicInteger written, AtomicBoolean waiti } public Flowable write(final byte[] header, final ByteArrayInputStream data) { - return write(header,data,Schedulers.newThread()); + return write(header, data, Schedulers.newThread()); } /** * Writes file to a device, atomic operation
+ * * @param header protobuf pftp operation bytes
* @param data actual file data
* @return Observable stream of byte offset progress
- * Produces: onNext current count of bytes written to device
- * onError any error during file transfer, @see BlePsFtpUtils for possible exceptions
- * onCompleted after file has been successfully been written to device, and device has
- * replyed ok
+ * Produces: onNext current count of bytes written to device
+ * onError any error during file transfer, @see BlePsFtpUtils for possible exceptions
+ * onCompleted after file has been successfully been written to device, and device has
+ * replyed ok
*/ public Flowable write(final byte[] header, final ByteArrayInputStream data, final Scheduler scheduler) { - return Flowable.create(new FlowableOnSubscribe() { - @Override - public void subscribe(@NonNull FlowableEmitter subscriber) throws Exception{ - // block until previous operation has completed - synchronized (pftpOperationMutex) { - if (pftpMtuEnabled.get() == ATT_SUCCESS) { - currentOperationWrite.set(true); - long pCounter = 0; - resetMtuPipe(); - // clear all before new operation - ByteArrayInputStream totalStream; - int headerSize = header.length; - totalStream = BlePsFtpUtils.makeCompleteMessageStream(new ByteArrayInputStream(header), data, BlePsFtpUtils.MessageType.REQUEST, 0); - int next = 0; - long totalPayload = totalStream.available(); - BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); - do { - byte[] airPacket; - int counter = 0; - try { - int temp = next; // workaround for stupid java translator idiotisim - airPacket = BlePsFtpUtils.buildRfc76MessageFrame(totalStream, temp, mtuSize.get(), sequenceNumber); - next = 1; - // transmit one frame - USE_ATTRIBUTE_LEVEL_RESPONSE.set( (++pCounter % (packetsCount.get())) == 0 ); - txInterface.transmitMessage(BlePsFtpClient.this,BlePsFtpUtils.RFC77_PFTP_SERVICE,BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC,airPacket,USE_ATTRIBUTE_LEVEL_RESPONSE.get()); - if (totalStream.available() != 0) { - if(USE_ATTRIBUTE_LEVEL_RESPONSE.get()) { - counter = 1; - packetsWrittenWithResponse.set(0); - waitPacketsWritten(packetsWrittenWithResponse, mtuWaiting, 1); - packetsWritten.set(0); - counter = 0; + return Flowable.create((FlowableOnSubscribe) subscriber -> { + // block until previous operation has completed + synchronized (pftpOperationMutex) { + if (pftpMtuEnabled.get() == ATT_SUCCESS) { + currentOperationWrite.set(true); + long pCounter = 0; + resetMtuPipe(); + // clear all before new operation + ByteArrayInputStream totalStream; + int headerSize = header.length; + totalStream = BlePsFtpUtils.makeCompleteMessageStream(new ByteArrayInputStream(header), data, BlePsFtpUtils.MessageType.REQUEST, 0); + int next = 0; + long totalPayload = totalStream.available(); + BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); + do { + byte[] airPacket; + int counter = 0; + try { + int temp = next; // workaround for stupid java translator idiotisim + airPacket = BlePsFtpUtils.buildRfc76MessageFrame(totalStream, temp, mtuSize.get(), sequenceNumber); + next = 1; + // transmit one frame + USE_ATTRIBUTE_LEVEL_RESPONSE.set((++pCounter % (packetsCount.get())) == 0); + txInterface.transmitMessage(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, airPacket, USE_ATTRIBUTE_LEVEL_RESPONSE.get()); + if (totalStream.available() != 0) { + if (USE_ATTRIBUTE_LEVEL_RESPONSE.get()) { + counter = 1; + packetsWrittenWithResponse.set(0); + waitPacketsWritten(packetsWrittenWithResponse, mtuWaiting, 1); + packetsWritten.set(0); + counter = 0; + } else { + ++counter; + } + // check input stream, give some time to respond + Pair packet = mtuInputQueue.poll(); + if (packet != null && packet.second == 0) { + BleLogger.e(TAG, "Frame sending interrupted by device!"); + BlePsFtpUtils.PftpRfc76ResponseHeader response = BlePsFtpUtils.processRfc76MessageFrameHeader(packet.first); + if (response.status == 0) { + if (!subscriber.isCancelled()) { + subscriber.tryOnError(new BlePsFtpUtils.PftpResponseError("Stream canceled: ", response.error)); + } + return; } else { - ++counter; - } - // check input stream, give some time to respond - Pair packet = mtuInputQueue.poll(); - if (packet != null && packet.second == 0) { - BleLogger.e(TAG, "Frame sending interrupted by device!"); - BlePsFtpUtils.PftpRfc76ResponseHeader response = BlePsFtpUtils.processRfc76MessageFrameHeader(packet.first); - if (response.status == 0) { - if(!subscriber.isCancelled()) { - subscriber.tryOnError(new BlePsFtpUtils.PftpResponseError("Stream canceled: ", response.error)); - } - return; - } else { - if(!subscriber.isCancelled()) { - subscriber.tryOnError(new Throwable("Stream canceled")); - } - return; + if (!subscriber.isCancelled()) { + subscriber.tryOnError(new Throwable("Stream canceled")); } + return; } } - long transferred = totalPayload - totalStream.available() - headerSize - 2; - //BleLogger.d(TAG, "Transferred: " + transferred); - subscriber.onNext(transferred); - } catch (InterruptedException ex) { - // Note RX throws InterruptedException when the stream is not completed, and it is unsubscribed - // canceled - BleLogger.e(TAG, "Frame sending interrupted!"); - handleMtuInterrupted(totalStream.available() != 0,counter); - return; - } catch (Throwable throwable) { - if(!subscriber.isCancelled()) { - subscriber.tryOnError(throwable); - } - return; } - } while (totalStream.available() != 0); - - // receive response - currentOperationWrite.set(false); - ByteArrayOutputStream response = new ByteArrayOutputStream(); - try { - readResponse(response); - } catch (InterruptedException ex){ - // catch interrupted as it cannot be rethrown onwards - BleLogger.e(TAG,"interrupted while reading response"); + long transferred = totalPayload - totalStream.available() - headerSize - 2; + //BleLogger.d(TAG, "Transferred: " + transferred); + subscriber.onNext(transferred); + } catch (InterruptedException ex) { + // Note RX throws InterruptedException when the stream is not completed, and it is unsubscribed + // canceled + BleLogger.e(TAG, "Frame sending interrupted!"); + handleMtuInterrupted(totalStream.available() != 0, counter); return; } catch (Throwable throwable) { - if(!subscriber.isCancelled()) { + if (!subscriber.isCancelled()) { subscriber.tryOnError(throwable); } return; } - subscriber.onComplete(); - } else if(!subscriber.isCancelled()) { - throw new BleCharacteristicNotificationNotEnabled("PS-FTP MTU not enabled"); + } while (totalStream.available() != 0); + + // receive response + currentOperationWrite.set(false); + ByteArrayOutputStream response = new ByteArrayOutputStream(); + try { + readResponse(response); + } catch (InterruptedException ex) { + // catch interrupted as it cannot be rethrown onwards + BleLogger.e(TAG, "interrupted while reading response"); + return; + } catch (Throwable throwable) { + if (!subscriber.isCancelled()) { + subscriber.tryOnError(throwable); + } + return; } + subscriber.onComplete(); + } else if (!subscriber.isCancelled()) { + throw new BleCharacteristicNotificationNotEnabled("PS-FTP MTU not enabled"); } } - }, BackpressureStrategy.LATEST).doOnSubscribe(new Consumer() { - @Override - public void accept(Subscription subscription) { - txInterface.gattClientRequestStopScanning(); - } - }).doFinally(new Action() { - @Override - public void run() { - txInterface.gattClientResumeScanning(); - currentOperationWrite.set(false); - } + }, BackpressureStrategy.LATEST).doOnSubscribe(subscription -> txInterface.gattClientRequestStopScanning()).doFinally(() -> { + txInterface.gattClientResumeScanning(); + currentOperationWrite.set(false); }).subscribeOn(scheduler).serialize(); } public Single query(final int id, final byte[] parameters) { - return query(id,parameters,Schedulers.newThread()); + return query(id, parameters, Schedulers.newThread()); } - private void handleMtuInterrupted(boolean dataAvailable, int lastRequs){ + private void handleMtuInterrupted(boolean dataAvailable, int lastRequs) { if (pftpMtuEnabled.get() == ATT_SUCCESS) { if (dataAvailable) { //wait for packets written @@ -425,9 +394,9 @@ private void handleMtuInterrupted(boolean dataAvailable, int lastRequs){ } txInterface.transmitMessages(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, Collections.singletonList(cancelPacket), USE_ATTRIBUTE_LEVEL_RESPONSE.get()); waitPacketsWritten(packetsWritten, mtuWaiting, 1); - BleLogger.d(TAG,"Stream cancel has been successfully send"); + BleLogger.d(TAG, "Stream cancel has been successfully send"); } catch (Throwable throwable) { - BleLogger.e(TAG,"Exception while trying to cancel streaming"); + BleLogger.e(TAG, "Exception while trying to cancel streaming"); } } } @@ -435,95 +404,90 @@ private void handleMtuInterrupted(boolean dataAvailable, int lastRequs){ /** * Sends a query to devicef - * @param id query id value + * + * @param id query id value * @param parameters optional parameters * @return Observable stream of response data
- * Produces: onNext, successful query response with or without data, depending on query type
- * onError, @see BlePsFtpUtils for possible exceptions
- * onCompleted, after successfully completed query
+ * Produces: onNext, successful query response with or without data, depending on query type
+ * onError, @see BlePsFtpUtils for possible exceptions
+ * onCompleted, after successfully completed query
*/ public Single query(final int id, final byte[] parameters, Scheduler scheduler) { - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter emitter) { - // block until previous operation has completed - try { - synchronized (pftpOperationMutex) { - if (pftpMtuEnabled.get() == ATT_SUCCESS) { - resetMtuPipe(); - ByteArrayInputStream totalStream = BlePsFtpUtils.makeCompleteMessageStream(parameters != null ? new ByteArrayInputStream(parameters) : null, null, BlePsFtpUtils.MessageType.QUERY, id); - BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); - List requs = BlePsFtpUtils.buildRfc76MessageFrameAll(totalStream, mtuSize.get(), sequenceNumber); - ByteArrayOutputStream response = new ByteArrayOutputStream(); - try { - txInterface.transmitMessages(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, requs, false); - waitPacketsWritten(packetsWritten, mtuWaiting, requs.size()); - requs.clear(); - readResponse(response); - emitter.onSuccess(response); - } catch (InterruptedException ex) { - // Note RX throws InterruptedException when the stream is not completed, and it is unsubscribed - // canceled - BleLogger.e(TAG, "Query interrupted"); - if (requs.size() == 0) { - handleMtuInterrupted(true, requs.size()); - } - if (!emitter.isDisposed()) { - throw ex; - } + return Single.create((SingleOnSubscribe) emitter -> { + // block until previous operation has completed + try { + synchronized (pftpOperationMutex) { + if (pftpMtuEnabled.get() == ATT_SUCCESS) { + resetMtuPipe(); + ByteArrayInputStream totalStream = BlePsFtpUtils.makeCompleteMessageStream(parameters != null ? new ByteArrayInputStream(parameters) : null, null, BlePsFtpUtils.MessageType.QUERY, id); + BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); + List requs = BlePsFtpUtils.buildRfc76MessageFrameAll(totalStream, mtuSize.get(), sequenceNumber); + ByteArrayOutputStream response = new ByteArrayOutputStream(); + try { + txInterface.transmitMessages(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, requs, false); + waitPacketsWritten(packetsWritten, mtuWaiting, requs.size()); + requs.clear(); + readResponse(response); + emitter.onSuccess(response); + } catch (InterruptedException ex) { + // Note RX throws InterruptedException when the stream is not completed, and it is unsubscribed + // canceled + BleLogger.e(TAG, "Query interrupted"); + if (requs.size() == 0) { + handleMtuInterrupted(true, requs.size()); + } + if (!emitter.isDisposed()) { + throw ex; } - } else { - throw new BleCharacteristicNotificationNotEnabled("PS-FTP MTU not enabled"); } - } - } catch (Exception ex) { - if (!emitter.isDisposed()) { - emitter.tryOnError(ex); + } else { + throw new BleCharacteristicNotificationNotEnabled("PS-FTP MTU not enabled"); } } + } catch (Exception ex) { + if (!emitter.isDisposed()) { + emitter.tryOnError(ex); + } } }).subscribeOn(scheduler); } - public Completable sendNotification(final int id, final byte[] parameters){ - return sendNotification(id,parameters,Schedulers.newThread()); + public Completable sendNotification(final int id, final byte[] parameters) { + return sendNotification(id, parameters, Schedulers.newThread()); } /** * Sends a single notification to device - * @param id one of the PbPFtpHostToDevNotification values + * + * @param id one of the PbPFtpHostToDevNotification values * @param parameters matching parameter for PbPFtpHostToDevNotification value if any * @return Flowable stream
- * Produces: onError, @see BlePsFtpUtils for possible exceptions
- * onComplete, once notification has been send
- * + * Produces: onError, @see BlePsFtpUtils for possible exceptions
+ * onComplete, once notification has been send
*/ - public Completable sendNotification(final int id, final byte[] parameters, Scheduler scheduler){ - return Completable.create(new CompletableOnSubscribe() { - @Override - public void subscribe(CompletableEmitter emitter) { - try { - synchronized (pftpNotificationMutex) { - if(txInterface.isConnected()) { - if(pftpD2HNotificationEnabled.get() == ATT_SUCCESS) { - resetNotificationPipe(); - ByteArrayInputStream totalStream = BlePsFtpUtils.makeCompleteMessageStream(parameters != null ? new ByteArrayInputStream(parameters) : null, null, BlePsFtpUtils.MessageType.NOTIFICATION, id); - BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); - List requs = BlePsFtpUtils.buildRfc76MessageFrameAll(totalStream, mtuSize.get(), sequenceNumber); - txInterface.transmitMessages(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_H2D_CHARACTERISTIC, requs, false); - waitPacketsWritten(notificationPacketsWritten, notificationWaiting, requs.size()); - emitter.onComplete(); - } else { - throw new BleCharacteristicNotificationNotEnabled("PS-FTP notification not enabled"); - } + public Completable sendNotification(final int id, final byte[] parameters, Scheduler scheduler) { + return Completable.create(emitter -> { + try { + synchronized (pftpNotificationMutex) { + if (txInterface.isConnected()) { + if (pftpD2HNotificationEnabled.get() == ATT_SUCCESS) { + resetNotificationPipe(); + ByteArrayInputStream totalStream = BlePsFtpUtils.makeCompleteMessageStream(parameters != null ? new ByteArrayInputStream(parameters) : null, null, BlePsFtpUtils.MessageType.NOTIFICATION, id); + BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); + List requs = BlePsFtpUtils.buildRfc76MessageFrameAll(totalStream, mtuSize.get(), sequenceNumber); + txInterface.transmitMessages(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_H2D_CHARACTERISTIC, requs, false); + waitPacketsWritten(notificationPacketsWritten, notificationWaiting, requs.size()); + emitter.onComplete(); } else { - throw new BleDisconnected(); + throw new BleCharacteristicNotificationNotEnabled("PS-FTP notification not enabled"); } + } else { + throw new BleDisconnected(); } - } catch (Exception ex){ - if(!emitter.isDisposed()){ - emitter.tryOnError(ex); - } + } + } catch (Exception ex) { + if (!emitter.isDisposed()) { + emitter.tryOnError(ex); } } }).subscribeOn(scheduler); @@ -531,101 +495,94 @@ public void subscribe(CompletableEmitter emitter) { /** * wait endlessly notifications + * * @param scheduler context where to run * @return Flowable stream - * Produces: onNext, for each complete notification received - * onError, @see BlePsFtpUtils - * onComplete, non produced + * Produces: onNext, for each complete notification received + * onError, @see BlePsFtpUtils + * onComplete, non produced */ - public Flowable waitForNotification(Scheduler scheduler){ - return Flowable.create(new FlowableOnSubscribe() { - @Override - public void subscribe(@NonNull FlowableEmitter subscriber) { - // NOTE no flush as client may be interested in buffered notifications - // notificationInputQueue.clear(); - synchronized (pftpWaitNotificationMutex) { - do { - if (pftpD2HNotificationEnabled.get() == ATT_SUCCESS) { - try { - synchronized (notificationInputQueue) { - if (notificationInputQueue.size() == 0) { - notificationInputQueue.wait(); - } + public Flowable waitForNotification(Scheduler scheduler) { + return Flowable.create((FlowableOnSubscribe) subscriber -> { + // NOTE no flush as client may be interested in buffered notifications + // notificationInputQueue.clear(); + synchronized (pftpWaitNotificationMutex) { + do { + if (pftpD2HNotificationEnabled.get() == ATT_SUCCESS) { + try { + synchronized (notificationInputQueue) { + if (notificationInputQueue.size() == 0) { + notificationInputQueue.wait(); } - } catch (InterruptedException ex){ - BleLogger.e(TAG,"Wait notification interrupted"); - return; - } - } else { - if(!subscriber.isCancelled()) { - subscriber.tryOnError(new BleCharacteristicNotificationNotEnabled("PS-FTP d2h notification not enabled")); } + } catch (InterruptedException ex) { + BleLogger.e(TAG, "Wait notification interrupted"); return; } - try { - Pair packet = notificationInputQueue.take(); - if (packet.second == 0) { - BlePsFtpUtils.PftpRfc76ResponseHeader response = BlePsFtpUtils.processRfc76MessageFrameHeader(packet.first); - if(response.next == 0) { - BlePsFtpUtils.PftpNotificationMessage notificationMessage = new BlePsFtpUtils.PftpNotificationMessage(); - notificationMessage.id = response.payload[0]; - notificationMessage.byteArrayOutputStream.write(response.payload, 1, response.payload.length - 1); - int status = response.status; - while (status == BlePsFtpUtils.RFC76_STATUS_MORE) { - packet = notificationInputQueue.poll(PROTOCOL_TIMEOUT, TimeUnit.SECONDS); - if (packet != null && packet.second == 0) { - response = BlePsFtpUtils.processRfc76MessageFrameHeader(packet.first); - status = response.status; - BleLogger.d(TAG, "Message frame sub sequent packet successfully received"); - notificationMessage.byteArrayOutputStream.write(response.payload, 0, response.payload.length); - } else { - // Failed to receive in timeline - if(!subscriber.isCancelled()) { - subscriber.tryOnError(new Throwable("Failed to receive notification packet in timeline")); - } - return; + } else { + if (!subscriber.isCancelled()) { + subscriber.tryOnError(new BleCharacteristicNotificationNotEnabled("PS-FTP d2h notification not enabled")); + } + return; + } + try { + Pair packet = notificationInputQueue.take(); + if (packet.second == 0) { + BlePsFtpUtils.PftpRfc76ResponseHeader response = BlePsFtpUtils.processRfc76MessageFrameHeader(packet.first); + if (response.next == 0) { + BlePsFtpUtils.PftpNotificationMessage notificationMessage = new BlePsFtpUtils.PftpNotificationMessage(); + notificationMessage.id = response.payload[0]; + notificationMessage.byteArrayOutputStream.write(response.payload, 1, response.payload.length - 1); + int status = response.status; + while (status == BlePsFtpUtils.RFC76_STATUS_MORE) { + packet = notificationInputQueue.poll(PROTOCOL_TIMEOUT, TimeUnit.SECONDS); + if (packet != null && packet.second == 0) { + response = BlePsFtpUtils.processRfc76MessageFrameHeader(packet.first); + status = response.status; + BleLogger.d(TAG, "Message frame sub sequent packet successfully received"); + notificationMessage.byteArrayOutputStream.write(response.payload, 0, response.payload.length); + } else { + // Failed to receive in timeline + if (!subscriber.isCancelled()) { + subscriber.tryOnError(new Throwable("Failed to receive notification packet in timeline")); } + return; } - subscriber.onNext(notificationMessage); - } else { - // not in sync - BleLogger.e(TAG,"wait notification not in sync, take next"); } + subscriber.onNext(notificationMessage); } else { - if(!subscriber.isCancelled()) { - subscriber.tryOnError(new BleAttributeError("ps-ftp wait notification failure ", packet.second)); - } - return; + // not in sync + BleLogger.e(TAG, "wait notification not in sync, take next"); } - } catch (InterruptedException ex) { - BleLogger.e(TAG,"wait notification interrupted"); - return; - } catch (Exception throwable) { - // continue to next notification or stop? - if(!subscriber.isCancelled()) { - subscriber.tryOnError(new Exception("Notification receive failed")); + } else { + if (!subscriber.isCancelled()) { + subscriber.tryOnError(new BleAttributeError("ps-ftp wait notification failure ", packet.second)); } return; } - } while (true); - } - } - },BackpressureStrategy.BUFFER).onBackpressureBuffer(100, new Action() { - @Override - public void run() { - BleLogger.w(TAG,"notifications buffer full"); + } catch (InterruptedException ex) { + BleLogger.e(TAG, "wait notification interrupted"); + return; + } catch (Exception throwable) { + // continue to next notification or stop? + if (!subscriber.isCancelled()) { + subscriber.tryOnError(new Exception("Notification receive failed")); + } + return; + } + } while (true); } - }, BackpressureOverflowStrategy.DROP_OLDEST).subscribeOn(scheduler).serialize(); + }, BackpressureStrategy.BUFFER).onBackpressureBuffer(100, () -> BleLogger.w(TAG, "notifications buffer full"), BackpressureOverflowStrategy.DROP_OLDEST).subscribeOn(scheduler).serialize(); } - public Completable waitPsFtpClientReady(final boolean checkConnection){ - return waitPsFtpClientReady(checkConnection,Schedulers.io()); + public Completable waitPsFtpClientReady(final boolean checkConnection) { + return waitPsFtpClientReady(checkConnection, Schedulers.io()); } - public Completable waitPsFtpClientReady(final boolean checkConnection,Scheduler scheduler){ - Completable mtuEnabled = waitNotificationEnabled(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC,checkConnection,scheduler); - Completable d2hEnabled = waitNotificationEnabled(BlePsFtpUtils.RFC77_PFTP_D2H_CHARACTERISTIC,checkConnection,scheduler); - return Completable.concatArray(mtuEnabled,d2hEnabled); + public Completable waitPsFtpClientReady(final boolean checkConnection, Scheduler scheduler) { + Completable mtuEnabled = waitNotificationEnabled(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, checkConnection, scheduler); + Completable d2hEnabled = waitNotificationEnabled(BlePsFtpUtils.RFC77_PFTP_D2H_CHARACTERISTIC, checkConnection, scheduler); + return Completable.concatArray(mtuEnabled, d2hEnabled); } private void readResponse(ByteArrayOutputStream outputStream) throws Exception { @@ -635,7 +592,7 @@ private void readResponse(ByteArrayOutputStream outputStream) throws Exception { BlePsFtpUtils.PftpRfc76ResponseHeader response = new BlePsFtpUtils.PftpRfc76ResponseHeader(); do { // wait for message frame - if( txInterface.isConnected() ) { + if (txInterface.isConnected()) { synchronized (mtuInputQueue) { if (mtuInputQueue.size() == 0) { mtuInputQueue.wait(PROTOCOL_TIMEOUT * 1000); @@ -648,18 +605,18 @@ private void readResponse(ByteArrayOutputStream outputStream) throws Exception { Pair packet = mtuInputQueue.poll(); if (packet != null && packet.second == 0) { BlePsFtpUtils.processRfc76MessageFrameHeader(response, packet.first); - if(sequenceNumber.getSeq() != response.sequenceNumber){ - if(response.status == BlePsFtpUtils.RFC76_STATUS_MORE){ + if (sequenceNumber.getSeq() != response.sequenceNumber) { + if (response.status == BlePsFtpUtils.RFC76_STATUS_MORE) { byte[] cancelPacket = new byte[]{0x00, 0x00, 0x00}; txInterface.transmitMessages(BlePsFtpClient.this, BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, Collections.singletonList(cancelPacket), true); waitPacketsWritten(packetsWritten, mtuWaiting, 1); - BleLogger.d(TAG,"Stream cancel has been successfully send"); + BleLogger.d(TAG, "Stream cancel has been successfully send"); } - throw new BlePsFtpUtils.PftpResponseError("Air packet lost!",303); + throw new BlePsFtpUtils.PftpResponseError("Air packet lost!", 303); } sequenceNumber.increment(); status = response.status; - if(next == response.next) { + if (next == response.next) { next = 1; switch (response.status) { case BlePsFtpUtils.RFC76_STATUS_LAST: @@ -667,32 +624,32 @@ private void readResponse(ByteArrayOutputStream outputStream) throws Exception { outputStream.write(response.payload, 0, response.payload.length); break; } - case BlePsFtpUtils.RFC76_STATUS_ERROR_OR_RESPONSE:{ // error or response + case BlePsFtpUtils.RFC76_STATUS_ERROR_OR_RESPONSE: { // error or response if (response.error == 0) { return; } throw new BlePsFtpUtils.PftpResponseError("Request failed: ", response.error); } default: - throw new BlePsFtpUtils.PftpResponseError("Protocol error, undefined status received",200); + throw new BlePsFtpUtils.PftpResponseError("Protocol error, undefined status received", 200); } - }else{ - throw new BlePsFtpUtils.PftpResponseError("Protocol error stream is out of sync",200); + } else { + throw new BlePsFtpUtils.PftpResponseError("Protocol error stream is out of sync", 200); } } else { handlePacketError(packet); } } while (status == BlePsFtpUtils.RFC76_STATUS_MORE); - BleLogger.d(TAG,"RFC76 message has read successfully"); + BleLogger.d(TAG, "RFC76 message has read successfully"); } private void handlePacketError(Pair packet) throws Exception { - if( !txInterface.isConnected() ) { + if (!txInterface.isConnected()) { // connection lost throw new BleDisconnected("Connection lost during packet read"); - }else if(packet == null){ + } else if (packet == null) { throw new BlePsFtpUtils.PftpOperationTimeout("Air packet was not received in required timeline"); - }else{ + } else { throw new BlePsFtpUtils.PftpResponseError("Response error: " + packet.second, packet.second); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/psftp/BlePsFtpUtils.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/psftp/BlePsFtpUtils.java index a5b6f61b..55556446 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/psftp/BlePsFtpUtils.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/api/ble/model/gatt/client/psftp/BlePsFtpUtils.java @@ -129,6 +129,7 @@ public static ByteArrayInputStream makeCompleteMessageStream( case REQUEST:{ int headerSize = header.available(); byte[] request = new byte[2]; + // RFC60 request[1] = (byte) ((headerSize & 0x7F00) >> 8); request[0] = (byte) (headerSize & 0x00FF); outputStream.write(request,0,2); @@ -140,6 +141,7 @@ public static ByteArrayInputStream makeCompleteMessageStream( } case QUERY:{ byte[] request = new byte[2]; + // RFC60 request[1] = (byte) (((id & 0x7F00) >> 8) | 0x80); request[0] = (byte) (id & 0x00FF); outputStream.write(request,0,2); diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/common/ble/RxUtils.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/common/ble/RxUtils.java index 358fa718..420842b2 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/common/ble/RxUtils.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/common/ble/RxUtils.java @@ -3,14 +3,14 @@ import com.androidcommunications.polar.api.ble.exceptions.BleDisconnected; import com.androidcommunications.polar.api.ble.model.gatt.BleGattTxInterface; import java.util.Set; -import io.reactivex.BackpressureStrategy; -import io.reactivex.CompletableEmitter; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; -import io.reactivex.ObservableEmitter; -import io.reactivex.SingleEmitter; -import io.reactivex.functions.Action; +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.CompletableEmitter; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; +import io.reactivex.rxjava3.core.FlowableOnSubscribe; +import io.reactivex.rxjava3.core.ObservableEmitter; +import io.reactivex.rxjava3.core.SingleEmitter; +import io.reactivex.rxjava3.functions.Action; public class RxUtils { diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/attribute/AttributeOperation.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/AttributeOperation.java similarity index 83% rename from sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/attribute/AttributeOperation.java rename to sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/AttributeOperation.java index b2a9fe7d..ac2ee26b 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/attribute/AttributeOperation.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/AttributeOperation.java @@ -1,4 +1,4 @@ -package com.androidcommunications.polar.enpoints.ble.common.attribute; +package com.androidcommunications.polar.enpoints.ble.bluedroid.host; import android.bluetooth.BluetoothGattCharacteristic; import android.support.annotation.NonNull; @@ -30,7 +30,7 @@ public enum AttributeOperationCommand{ private boolean enable=false; private boolean withResponse=false; - public void setIsPartOfPrimaryService(boolean isPartOfPrimaryService) { + void setIsPartOfPrimaryService(boolean isPartOfPrimaryService) { this.isPartOfPrimaryService = isPartOfPrimaryService; } @@ -38,13 +38,13 @@ private boolean isPartOfPrimaryService() { return isPartOfPrimaryService; } - public AttributeOperation(AttributeOperationCommand attributeOperation, + AttributeOperation(AttributeOperationCommand attributeOperation, BluetoothGattCharacteristic characteristic) { this.attributeOperation = attributeOperation; this.characteristic = characteristic; } - public AttributeOperation(AttributeOperationCommand attributeOperation, + AttributeOperation(AttributeOperationCommand attributeOperation, BluetoothGattCharacteristic characteristic, boolean enable) { this.attributeOperation = attributeOperation; @@ -52,7 +52,7 @@ public AttributeOperation(AttributeOperationCommand attributeOperation, this.enable = enable; } - public AttributeOperation(AttributeOperationCommand attributeOperation, + AttributeOperation(AttributeOperationCommand attributeOperation, byte[] data, BluetoothGattCharacteristic characteristic, final boolean withResponse) { @@ -62,7 +62,7 @@ public AttributeOperation(AttributeOperationCommand attributeOperation, this.withResponse = withResponse; } - public AttributeOperationCommand getAttributeOperation() { + AttributeOperationCommand getAttributeOperation() { return attributeOperation; } @@ -78,7 +78,7 @@ public boolean isEnable() { return enable; // notification or indication } - public boolean isWithResponse() { + boolean isWithResponse() { return withResponse; } -} +} \ No newline at end of file diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDBondingListener.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDBondingListener.java index 328ef30f..75e7f088 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDBondingListener.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDBondingListener.java @@ -14,7 +14,9 @@ class BDBondingListener { interface AuthenticationObserverInterface { // bonding completion void bonding(); + void bonded(); + void bondNone(); } @@ -26,17 +28,17 @@ interface AuthenticationObserverInterface { this.context = context; IntentFilter intent = new IntentFilter(); intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); - context.registerReceiver(mReceiver,intent); + context.registerReceiver(mReceiver, intent); } - void stopBroadcastReceiver(){ - if(mReceiver!=null) { + void stopBroadcastReceiver() { + if (mReceiver != null) { context.unregisterReceiver(mReceiver); mReceiver = null; } } - static abstract class BondingObserver implements AuthenticationObserverInterface{ + static abstract class BondingObserver implements AuthenticationObserverInterface { private BluetoothDevice device; BondingObserver(BluetoothDevice device) { @@ -48,11 +50,11 @@ public BluetoothDevice getDevice() { } } - void addObserver(BondingObserver observer){ + void addObserver(BondingObserver observer) { authenticationObservers.add(observer); } - void removeObserver(BondingObserver observer){ + void removeObserver(BondingObserver observer) { authenticationObservers.remove(observer); } @@ -61,38 +63,29 @@ void removeObserver(BondingObserver observer){ public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if(device != null && action != null) { + if (device != null && action != null) { if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); - BleLogger.d(TAG,"Bond manager state:" + state + " action: " + intent.toString()); + BleLogger.d(TAG, "Bond manager state:" + state + " action: " + intent.toString()); switch (state) { case BluetoothDevice.BOND_BONDING: - authenticationObservers.accessAll(new AtomicSet.ObjectAccess() { - @Override - public void access(BondingObserver object) { - if( object.getDevice().equals(device) ) { - object.bonding(); - } + authenticationObservers.accessAll(object -> { + if (object.getDevice().equals(device)) { + object.bonding(); } }); break; case BluetoothDevice.BOND_BONDED: - authenticationObservers.accessAll(new AtomicSet.ObjectAccess() { - @Override - public void access(BondingObserver object) { - if( object.getDevice().equals(device) ) { - object.bonded(); - } + authenticationObservers.accessAll(object -> { + if (object.getDevice().equals(device)) { + object.bonded(); } }); break; case BluetoothDevice.BOND_NONE: - authenticationObservers.accessAll(new AtomicSet.ObjectAccess() { - @Override - public void access(BondingObserver object) { - if( object.getDevice().equals(device) ) { - object.bondNone(); - } + authenticationObservers.accessAll(object -> { + if (object.getDevice().equals(device)) { + object.bondNone(); } }); break; diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceList.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceList.java index 9c05ba38..6a3ac6ae 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceList.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceList.java @@ -19,54 +19,36 @@ AtomicSet getSessions() { return sessions; } - BDDeviceSessionImpl getSession(final BluetoothDevice device){ - return sessions.fetch(new AtomicSet.CompareFunction() { - @Override - public boolean compare(BDDeviceSessionImpl object) { - return object.getBluetoothDevice().getAddress().equals(device.getAddress()); - } - }); + BDDeviceSessionImpl getSession(final BluetoothDevice device) { + return sessions.fetch(object -> object.getBluetoothDevice().getAddress().equals(device.getAddress())); } - void addSession(BDDeviceSessionImpl smartPolarDeviceSession){ + void addSession(BDDeviceSessionImpl smartPolarDeviceSession) { BleLogger.d(TAG, "new session added: " + smartPolarDeviceSession.getAdvertisementContent().getName()); sessions.add(smartPolarDeviceSession); } - Set copyDeviceList(){ - return new HashSet(sessions.objects()); + Set copyDeviceList() { + return new HashSet<>(sessions.objects()); } - BDDeviceSessionImpl getSession(final BluetoothGatt gatt){ - return sessions.fetch(new AtomicSet.CompareFunction() { - @Override - public boolean compare(BDDeviceSessionImpl object) { - synchronized (object.getGattMutex()) { - return object.getGatt() != null && object.getGatt().equals(gatt); - } + BDDeviceSessionImpl getSession(final BluetoothGatt gatt) { + return sessions.fetch(object -> { + synchronized (object.getGattMutex()) { + return object.getGatt() != null && object.getGatt().equals(gatt); } }); } - BDDeviceSessionImpl getSession(final String address){ - return sessions.fetch(new AtomicSet.CompareFunction() { - @Override - public boolean compare(BDDeviceSessionImpl object) { - return object.getAddress().equals(address); - } - }); + BDDeviceSessionImpl getSession(final String address) { + return sessions.fetch(object -> object.getAddress().equals(address)); } - interface CompareFunction{ + interface CompareFunction { boolean compare(BDDeviceSessionImpl smartPolarDeviceSession1); } - BDDeviceSessionImpl fetch(final CompareFunction function){ - return sessions.fetch(new AtomicSet.CompareFunction() { - @Override - public boolean compare(BDDeviceSessionImpl object) { - return function.compare(object); - } - }); + BDDeviceSessionImpl fetch(final CompareFunction function) { + return sessions.fetch(function::compare); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceListenerImpl.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceListenerImpl.java index bdc842cc..c80e8d11 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceListenerImpl.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceListenerImpl.java @@ -8,7 +8,9 @@ import android.bluetooth.le.ScanFilter; import android.content.Context; import android.os.Build; +import android.support.annotation.Nullable; +import com.androidcommunications.polar.api.ble.BleDeviceListener; import com.androidcommunications.polar.api.ble.BleLogger; import com.androidcommunications.polar.api.ble.exceptions.BleStartScanError; import com.androidcommunications.polar.api.ble.model.BleDeviceSession; @@ -17,10 +19,12 @@ import com.androidcommunications.polar.common.ble.AtomicSet; import com.androidcommunications.polar.common.ble.BleUtils; import com.androidcommunications.polar.common.ble.RxUtils; -import com.androidcommunications.polar.enpoints.ble.common.BleDeviceListener2; -import com.androidcommunications.polar.enpoints.ble.common.BleDeviceSession2; -import com.androidcommunications.polar.enpoints.ble.common.connection.ConnectionHandlerObserver; +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ConnectionHandler; +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ConnectionHandlerObserver; +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ConnectionInterface; +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ScannerInterface; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -28,17 +32,19 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import io.reactivex.BackpressureOverflowStrategy; -import io.reactivex.BackpressureStrategy; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; -import io.reactivex.annotations.NonNull; -import io.reactivex.functions.Action; -import io.reactivex.processors.BehaviorProcessor; -import io.reactivex.processors.FlowableProcessor; +import io.reactivex.rxjava3.core.BackpressureOverflowStrategy; +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.FlowableEmitter; +import io.reactivex.rxjava3.core.FlowableOnSubscribe; +import io.reactivex.rxjava3.annotations.NonNull; -public class BDDeviceListenerImpl extends BleDeviceListener2 implements BDScanCallback.BDScanCallbackInterface { +public class BDDeviceListenerImpl extends BleDeviceListener implements + BDScanCallback.BDScanCallbackInterface, + ScannerInterface, + ConnectionInterface, + BDPowerListener.BlePowerState, + ConnectionHandlerObserver { private final static String TAG = BDDeviceListenerImpl.class.getSimpleName(); private BluetoothAdapter bluetoothAdapter; @@ -49,98 +55,34 @@ public class BDDeviceListenerImpl extends BleDeviceListener2 implements BDScanCa private BDPowerListener powerManager; private BDBondingListener bondingManager; private AtomicSet> observers = new AtomicSet<>(); - private FlowableProcessor powerObservers = BehaviorProcessor.create(); + private ConnectionHandler connectionHandler; + private Context context; + private BleDeviceSessionStateChangedCallback changedCallback = null; + private BlePowerStateChangedCallback powerStateChangedCallback = null; public BDDeviceListenerImpl( @NonNull final Context context, - @NonNull Set > clients){ - super(context,clients); + @NonNull Set> clients) { + super(clients); + this.context = context; btManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); if (btManager != null) { bluetoothAdapter = btManager.getAdapter(); } - gattCallback = new BDGattCallback(context,connectionHandler,sessions); + connectionHandler = new ConnectionHandler(context, this, this, this); + gattCallback = new BDGattCallback(context, connectionHandler, sessions); bondingManager = new BDBondingListener(context); scanCallback = new BDScanCallback(context, btManager, this); - connectionHandler.addObserver(new ConnectionHandlerObserver() { - @Override - public void deviceSessionStateChanged(BleDeviceSession2 session) { - if( sessions.fetch(new BDDeviceList.CompareFunction() { - @Override - public boolean compare(BDDeviceSessionImpl smartPolarDeviceSession1) { - return smartPolarDeviceSession1.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK; - } - }) != null ){ - scanCallback.clientAdded(); - } else { - scanCallback.clientRemoved(); - } - } - - @Override - public void deviceConnected(BleDeviceSession2 session) { - - } - - @Override - public void deviceDisconnected(BleDeviceSession2 session) { - - } - - @Override - public void deviceConnectionCancelled(BleDeviceSession2 session) { - - } - }); - powerManager = new BDPowerListener(bluetoothAdapter, context, new BDPowerListener.BlePowerState() { - @Override - public void blePoweredOff() { - BleLogger.e(TAG, "BLE powered off"); - powerObservers.onNext(false); - scanCallback.powerOff(); - if(powerStateChangedCallback!=null) { - powerStateChangedCallback.stateChanged(false); - } - for(BDDeviceSessionImpl deviceSession : sessions.getSessions().objects()){ - switch (deviceSession.getSessionState()) { - case SESSION_OPEN: - case SESSION_OPENING: - case SESSION_CLOSING:{ - gattCallback.onConnectionStateChange(deviceSession.getGatt(),0, BluetoothGatt.STATE_DISCONNECTED); - break; - } - default:{ - connectionHandler.deviceDisconnected(deviceSession); - break; - } - } - } - } - - @Override - public void blePoweredOn() { - BleLogger.d(TAG,"BLE powered on!"); - scanCallback.powerOn(); - powerObservers.onNext(true); - if(powerStateChangedCallback!=null) { - powerStateChangedCallback.stateChanged(true); - } - } - }); + powerManager = new BDPowerListener(bluetoothAdapter, context, this); } @Override - public boolean bleActive(){ + public boolean bleActive() { return bluetoothAdapter != null && bluetoothAdapter.isEnabled(); } @Override - public Flowable monitorBleState() { - return powerObservers.toSerialized(); - } - - @Override - public void setScanFilters(final List filters){ + public void setScanFilters(final List filters) { scanCallback.setScanFilters(filters); } @@ -155,50 +97,41 @@ public void setOpportunisticScan(boolean disable) { } @Override - public Flowable search(final boolean fetchKnownDevices){ + public Flowable search(final boolean fetchKnownDevices) { final FlowableEmitter[] subscriber1 = new FlowableEmitter[1]; - return Flowable.create(new FlowableOnSubscribe() { - @Override - public void subscribe(@NonNull FlowableEmitter subscriber) { - if( fetchKnownDevices ) { - List devices = - btManager.getDevicesMatchingConnectionStates(BluetoothProfile.GATT, - new int[]{BluetoothProfile.STATE_CONNECTED | BluetoothProfile.STATE_CONNECTING}); - for (BluetoothDevice device : devices) { - if (device.getType() == BluetoothDevice.DEVICE_TYPE_LE && sessions.getSession(device) == null) { - BDDeviceSessionImpl newDevice = new BDDeviceSessionImpl(context, device, scanCallback, bondingManager, factory); - sessions.addSession(newDevice); - } + return Flowable.create((FlowableOnSubscribe) subscriber -> { + if (fetchKnownDevices) { + List devices = + btManager.getDevicesMatchingConnectionStates(BluetoothProfile.GATT, + new int[]{BluetoothProfile.STATE_CONNECTED | BluetoothProfile.STATE_CONNECTING}); + for (BluetoothDevice device : devices) { + if (device.getType() == BluetoothDevice.DEVICE_TYPE_LE && sessions.getSession(device) == null) { + BDDeviceSessionImpl newDevice = new BDDeviceSessionImpl(context, device, scanCallback, bondingManager, factory); + sessions.addSession(newDevice); } - Set bondedDevices = bluetoothAdapter.getBondedDevices(); - for (BluetoothDevice device : bondedDevices) { - if (device.getType() == BluetoothDevice.DEVICE_TYPE_LE && sessions.getSession(device) == null) { - BDDeviceSessionImpl newDevice = new BDDeviceSessionImpl(context, device, scanCallback, bondingManager, factory); - sessions.addSession(newDevice); - } - } - Set sessionsList = sessions.copyDeviceList(); - for (BleDeviceSession deviceSession : sessionsList) { - subscriber.onNext(deviceSession); + } + Set bondedDevices = bluetoothAdapter.getBondedDevices(); + for (BluetoothDevice device : bondedDevices) { + if (device.getType() == BluetoothDevice.DEVICE_TYPE_LE && sessions.getSession(device) == null) { + BDDeviceSessionImpl newDevice = new BDDeviceSessionImpl(context, device, scanCallback, bondingManager, factory); + sessions.addSession(newDevice); } } - - subscriber1[0] = subscriber; - observers.add(subscriber); - scanCallback.clientAdded(); - } - }, BackpressureStrategy.BUFFER).onBackpressureBuffer(200, new Action() { - @Override - public void run() { - // do nothing - BleLogger.w(TAG,"search backpressure buffer full"); - } - }, BackpressureOverflowStrategy.DROP_OLDEST).doFinally(new Action() { - @Override - public void run() { - observers.remove(subscriber1[0]); - scanCallback.clientRemoved(); + Set sessionsList = sessions.copyDeviceList(); + for (BleDeviceSession deviceSession : sessionsList) { + subscriber.onNext(deviceSession); + } } + + subscriber1[0] = subscriber; + observers.add(subscriber); + scanCallback.clientAdded(); + }, BackpressureStrategy.BUFFER).onBackpressureBuffer(200, () -> { + // do nothing + BleLogger.w(TAG, "search backpressure buffer full"); + }, BackpressureOverflowStrategy.DROP_OLDEST).doFinally(() -> { + observers.remove(subscriber1[0]); + scanCallback.clientRemoved(); }); } @@ -208,17 +141,11 @@ public void setMtu(int mtu) { } @Override - public void shutDown(){ - super.shutDown(); + public void shutDown() { bondingManager.stopBroadcastReceiver(); powerManager.stopBroadcastReceiver(); scanCallback.stopScan(); - sessions.getSessions().accessAll(new AtomicSet.ObjectAccess() { - @Override - public void access(BDDeviceSessionImpl object) { - object.resetGatt(); - } - }); + sessions.getSessions().accessAll(BDDeviceSessionImpl::resetGatt); sessions.getSessions().clear(); } @@ -230,8 +157,8 @@ public Set deviceSessions() { @Override public BleDeviceSession sessionByAddress(String address) { BDDeviceSessionImpl session = sessions.getSession(address); - if(session == null){ - session = new BDDeviceSessionImpl(context,bluetoothAdapter.getRemoteDevice(address),scanCallback,bondingManager,factory); + if (session == null) { + session = new BDDeviceSessionImpl(context, bluetoothAdapter.getRemoteDevice(address), scanCallback, bondingManager, factory); sessions.addSession(session); } return session; @@ -239,17 +166,17 @@ public BleDeviceSession sessionByAddress(String address) { @Override public boolean removeSession(BleDeviceSession deviceSession) { - if(deviceSession.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSED && - !deviceSession.isAdvertising(30,TimeUnit.SECONDS) && - sessions.getSessions().contains((BDDeviceSessionImpl) deviceSession)){ - sessions.getSessions().remove((BDDeviceSessionImpl)deviceSession); + if (deviceSession.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSED && + !deviceSession.isAdvertising(30, TimeUnit.SECONDS) && + sessions.getSessions().contains((BDDeviceSessionImpl) deviceSession)) { + sessions.getSessions().remove((BDDeviceSessionImpl) deviceSession); return true; } return false; } @Override - public int removeAllSessions(){ + public int removeAllSessions() { return removeAllSessions(new HashSet<>(Collections.singletonList(BleDeviceSession.DeviceSessionState.SESSION_CLOSED))); } @@ -257,10 +184,10 @@ public int removeAllSessions(){ public int removeAllSessions(Set inStates) { int count = 0; Set list = sessions.copyDeviceList(); - for(BleDeviceSession session : list){ - if( inStates.contains(session.getSessionState()) && - sessions.getSessions().contains((BDDeviceSessionImpl) session) ){ - sessions.getSessions().remove((BDDeviceSessionImpl)session); + for (BleDeviceSession session : list) { + if (inStates.contains(session.getSessionState()) && + sessions.getSessions().contains((BDDeviceSessionImpl) session)) { + sessions.getSessions().remove((BDDeviceSessionImpl) session); count += 1; } } @@ -268,41 +195,38 @@ public int removeAllSessions(Set inStates) } @Override - public void connectDevice(final BleDeviceSession2 session) { - BDDeviceSessionImpl btSmartDeviceSession = (BDDeviceSessionImpl)session; + public void connectDevice(final BDDeviceSessionImpl session) { BluetoothGatt gatt; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - int mask=BluetoothDevice.PHY_LE_1M_MASK; - if( bluetoothAdapter.isLe2MPhySupported() ) mask |= BluetoothDevice.PHY_LE_2M_MASK; - gatt = btSmartDeviceSession.getBluetoothDevice().connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE, mask ); + int mask = BluetoothDevice.PHY_LE_1M_MASK; + if (bluetoothAdapter.isLe2MPhySupported()) mask |= BluetoothDevice.PHY_LE_2M_MASK; + gatt = session.getBluetoothDevice().connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE, mask); } else { - gatt = btSmartDeviceSession.getBluetoothDevice().connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE); + gatt = session.getBluetoothDevice().connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE); } - }else{ - gatt = btSmartDeviceSession.getBluetoothDevice().connectGatt(context, false, gattCallback ); + } else { + gatt = session.getBluetoothDevice().connectGatt(context, false, gattCallback); } - synchronized (btSmartDeviceSession.getGattMutex()) { - btSmartDeviceSession.setGatt(gatt); + synchronized (session.getGattMutex()) { + session.setGatt(gatt); } } @Override - public void disconnectDevice(final BleDeviceSession2 session) { - BDDeviceSessionImpl btSmartDeviceSession = (BDDeviceSessionImpl) session; - synchronized (btSmartDeviceSession.getGattMutex()) { - if (btSmartDeviceSession.getGatt() != null) { - btSmartDeviceSession.getGatt().disconnect(); + public void disconnectDevice(final BDDeviceSessionImpl session) { + synchronized (session.getGattMutex()) { + if (session.getGatt() != null) { + session.getGatt().disconnect(); } } } @Override - public void cancelDeviceConnection(BleDeviceSession2 session) { - BDDeviceSessionImpl btSmartDeviceSession = (BDDeviceSessionImpl)session; - synchronized (btSmartDeviceSession.getGattMutex()) { - if (btSmartDeviceSession.getGatt() != null) { - btSmartDeviceSession.getGatt().disconnect(); + public void cancelDeviceConnection(BDDeviceSessionImpl session) { + synchronized (session.getGattMutex()) { + if (session.getGatt() != null) { + session.getGatt().disconnect(); } } } @@ -318,15 +242,15 @@ public void connectionHandlerRequestStopScanning() { } @Override - public void setPowerMode(@PowerMode int mode){ - switch (mode){ - case POWER_MODE_NORMAL:{ + public void setPowerMode(@PowerMode int mode) { + switch (mode) { + case POWER_MODE_NORMAL: { scanCallback.stopScan(); scanCallback.setLowPowerEnabled(false); scanCallback.startScan(); break; } - case POWER_MODE_LOW:{ + case POWER_MODE_LOW: { scanCallback.stopScan(); scanCallback.setLowPowerEnabled(true); scanCallback.startScan(); @@ -338,7 +262,7 @@ public void setPowerMode(@PowerMode int mode){ @Override public void deviceDiscovered(BluetoothDevice device, int rssi, byte[] scanRecord, BleUtils.EVENT_TYPE type) { BDDeviceSessionImpl deviceSession = sessions.getSession(device); - HashMap advData = BleUtils.advertisementBytes2Map(scanRecord); + HashMap advData = BleUtils.advertisementBytes2Map(scanRecord); final String manufacturer = Build.MANUFACTURER; final String name = device.getName(); if (name != null && manufacturer.equalsIgnoreCase("samsung")) { @@ -347,21 +271,16 @@ public void deviceDiscovered(BluetoothDevice device, int rssi, byte[] scanRecord advData.put(BleUtils.AD_TYPE.GAP_ADTYPE_LOCAL_NAME_COMPLETE, name.getBytes()); } - if( deviceSession == null ){ + if (deviceSession == null) { final BleAdvertisementContent content = new BleAdvertisementContent(); - content.processAdvertisementData(advData,type, rssi); - if(preFilter == null || preFilter.process(content)) { + content.processAdvertisementData(advData, type, rssi); + if (preFilter == null || preFilter.process(content)) { if (content.getPolarHrAdvertisement().isPresent() && content.getPolarDeviceIdInt() != 0 && (content.getPolarDeviceType().equals("H10") || - content.getPolarDeviceType().equals("H9"))) { + content.getPolarDeviceType().equals("H9"))) { // check if old can be found, NOTE this is a special case for H10 only (random static address) - BDDeviceSessionImpl oldSession = sessions.fetch(new BDDeviceList.CompareFunction() { - @Override - public boolean compare(BDDeviceSessionImpl smartPolarDeviceSession1) { - return smartPolarDeviceSession1.getAdvertisementContent().getPolarDeviceId().equals(content.getPolarDeviceId()); - } - }); + BDDeviceSessionImpl oldSession = sessions.fetch(smartPolarDeviceSession1 -> smartPolarDeviceSession1.getAdvertisementContent().getPolarDeviceId().equals(content.getPolarDeviceId())); if (oldSession != null && (oldSession.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSED || oldSession.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK)) { BleLogger.d(TAG, "old polar device found name: " + oldSession.getAdvertisementContent().getName() + " dev name: " + device.getName() + " old name: " + oldSession.getBluetoothDevice().getName() + " old addr: " + oldSession.getAddress() + " device: " + device.toString()); @@ -380,32 +299,127 @@ public boolean compare(BDDeviceSessionImpl smartPolarDeviceSession1) { // device is not desired return; } - }else{ + } else { deviceSession.getAdvertisementContent().processAdvertisementData(advData, type, rssi); } connectionHandler.advertisementHeadReceived(deviceSession); final BDDeviceSessionImpl finalDeviceSession = deviceSession; - RxUtils.emitNext(observers, new RxUtils.Emitter>() { - @Override - public void item(FlowableEmitter object) { - object.onNext(finalDeviceSession); - } - }); + RxUtils.emitNext(observers, object -> object.onNext(finalDeviceSession)); } @Override public void scanStartError(int error) { - RxUtils.postError(observers,new BleStartScanError("scan start failed ", error)); + RxUtils.postError(observers, new BleStartScanError("scan start failed ", error)); } @Override public boolean isScanningNeeded() { - return observers.size() != 0 || sessions.fetch(new BDDeviceList.CompareFunction() { - @Override - public boolean compare(BDDeviceSessionImpl smartPolarDeviceSession1) { - return smartPolarDeviceSession1.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK; + return observers.size() != 0 || sessions.fetch(smartPolarDeviceSession1 -> smartPolarDeviceSession1.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK) != null; + } + + + @Override + public void setBlePowerStateCallback(@Nullable BlePowerStateChangedCallback cb) { + this.powerStateChangedCallback = cb; + if (cb != null) { + cb.stateChanged(this.bleActive()); + } + } + + @Override + public void setDeviceSessionStateChangedCallback(@Nullable BleDeviceSessionStateChangedCallback changedCallback) { + this.changedCallback = changedCallback; + } + + @Override + public void openSessionDirect(BleDeviceSession session) { + session.setConnectionUuids(new ArrayList<>()); + connectionHandler.connectDevice((BDDeviceSessionImpl) session, bleActive()); + } + + @Override + public void openSessionDirect(BleDeviceSession session, List uuids) { + session.setConnectionUuids(uuids); + connectionHandler.connectDevice((BDDeviceSessionImpl) session, bleActive()); + } + + @Override + public void closeSessionDirect(BleDeviceSession session) { + connectionHandler.disconnectDevice((BDDeviceSessionImpl) session); + } + + @Override + public void setAutomaticReconnection(boolean automaticReconnection) { + connectionHandler.setAutomaticReconnection(automaticReconnection); + } + + @Override + public void blePoweredOff() { + BleLogger.e(TAG, "BLE powered off"); + scanCallback.powerOff(); + if (powerStateChangedCallback != null) { + powerStateChangedCallback.stateChanged(false); + } + for (BDDeviceSessionImpl deviceSession : sessions.getSessions().objects()) { + switch (deviceSession.getSessionState()) { + case SESSION_OPEN: + case SESSION_OPENING: + case SESSION_CLOSING: { + gattCallback.onConnectionStateChange(deviceSession.getGatt(), 0, BluetoothGatt.STATE_DISCONNECTED); + break; + } + default: { + connectionHandler.deviceDisconnected(deviceSession); + break; + } + } + } + } + + @Override + public void blePoweredOn() { + BleLogger.d(TAG, "BLE powered on"); + scanCallback.powerOn(); + if (powerStateChangedCallback != null) { + powerStateChangedCallback.stateChanged(true); + } + } + + @Override + public void deviceSessionStateChanged(BDDeviceSessionImpl session) { + if (sessions.fetch(smartPolarDeviceSession1 -> smartPolarDeviceSession1.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK) != null) { + scanCallback.clientAdded(); + } else { + scanCallback.clientRemoved(); + } + if (changedCallback != null) { + if (session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK && + session.getPreviousState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN) { + //NOTE special case, we were connected so propagate closed event( informal ) + changedCallback.stateChanged(session, BleDeviceSession.DeviceSessionState.SESSION_CLOSED); + if (session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK) { + changedCallback.stateChanged(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); + } + } else { + changedCallback.stateChanged(session, session.getSessionState()); } - }) != null; + } + } + + @Override + public void deviceConnected(BDDeviceSessionImpl session) { + + } + + @Override + public void deviceDisconnected(BDDeviceSessionImpl session) { + session.handleDisconnection(); + session.reset(); + } + + @Override + public void deviceConnectionCancelled(BDDeviceSessionImpl session) { + session.reset(); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceSessionImpl.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceSessionImpl.java index b8469363..99b8738a 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceSessionImpl.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDDeviceSessionImpl.java @@ -7,6 +7,7 @@ import android.bluetooth.BluetoothGattService; import android.content.Context; import android.os.Handler; +import android.support.annotation.VisibleForTesting; import com.androidcommunications.polar.BuildConfig; import com.androidcommunications.polar.api.ble.BleLogger; @@ -21,8 +22,6 @@ import com.androidcommunications.polar.api.ble.model.gatt.BleGattTxInterface; import com.androidcommunications.polar.common.ble.AtomicSet; import com.androidcommunications.polar.common.ble.RxUtils; -import com.androidcommunications.polar.enpoints.ble.common.BleDeviceSession2; -import com.androidcommunications.polar.enpoints.ble.common.attribute.AttributeOperation; import java.lang.reflect.Method; import java.util.ArrayList; @@ -33,18 +32,15 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -import io.reactivex.Completable; -import io.reactivex.CompletableEmitter; -import io.reactivex.CompletableOnSubscribe; -import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.functions.Action; -import io.reactivex.functions.Consumer; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.core.SingleEmitter; +import io.reactivex.rxjava3.core.SingleOnSubscribe; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.functions.Action; -class BDDeviceSessionImpl extends BleDeviceSession2 implements BleGattTxInterface { +public class BDDeviceSessionImpl extends BleDeviceSession implements BleGattTxInterface { private static final UUID DESCRIPTOR_CCC = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); private final static String TAG = BDDeviceSessionImpl.class.getSimpleName(); @@ -64,11 +60,14 @@ class BDDeviceSessionImpl extends BleDeviceSession2 implements BleGattTxInterfac private Context context; private Handler handler; - BDDeviceSessionImpl(Context context, - BluetoothDevice bluetoothDevice, - BDScanCallback scanCallback, - BDBondingListener bondingManager, - BleGattFactory factory){ + @VisibleForTesting + public BDDeviceSessionImpl(){} + + public BDDeviceSessionImpl(Context context, + BluetoothDevice bluetoothDevice, + BDScanCallback scanCallback, + BDBondingListener bondingManager, + BleGattFactory factory) { super(); this.context = context; this.handler = new Handler(context.getMainLooper()); @@ -90,7 +89,7 @@ void setBluetoothDevice(BluetoothDevice bluetoothDevice) { } BluetoothGatt getGatt() { - return gatt; + return gatt; } void setGatt(BluetoothGatt gatt) { @@ -115,17 +114,32 @@ void resetGatt() { try { //gatt.disconnect(); gatt.close(); - } catch(Exception e) { - BleLogger.e(TAG,"gatt error: " + e.toString()); + } catch (Exception e) { + BleLogger.e(TAG, "gatt error: " + e.toString()); } } gatt = null; } } - @Override - public void reset(){ - BleLogger.d(TAG,"reset"); + private void logIfError(final String message, int status) { + if (status != 0) { + BleLogger.e(TAG, message + " Failed with error: " + status); + } + } + + /** + * Internal use only + * + * @param sessionState @see BleDeviceSession.DeviceSessionState + */ + public void setSessionState(BleDeviceSession.DeviceSessionState sessionState) { + this.previousState = this.state; + this.state = sessionState; + } + + public void reset() { + BleLogger.d(TAG, "reset"); resetGatt(); } @@ -142,57 +156,49 @@ public String getAddress() { @Override public Completable authenticate() { final BDBondingListener.BondingObserver[] observer = {null}; - return Completable.create(new CompletableOnSubscribe() { - @Override - public void subscribe(final CompletableEmitter subscriber) { - if( getSessionState() == DeviceSessionState.SESSION_OPEN ) { - switch (bluetoothDevice.getBondState()) { - case BluetoothDevice.BOND_NONE: { - if( !bluetoothDevice.createBond() ){ - subscriber.tryOnError(new Throwable("BD bonding start failed")); - return; - } + return Completable.create(subscriber -> { + if (getSessionState() == DeviceSessionState.SESSION_OPEN) { + switch (bluetoothDevice.getBondState()) { + case BluetoothDevice.BOND_NONE: { + if (!bluetoothDevice.createBond()) { + subscriber.tryOnError(new Throwable("BD bonding start failed")); + return; } - case BluetoothDevice.BOND_BONDING: { - observer[0] = new BDBondingListener.BondingObserver(bluetoothDevice) { - @Override - public void bonding() { - //subscriber.onNext(new HashMap()); - } + } + case BluetoothDevice.BOND_BONDING: { + observer[0] = new BDBondingListener.BondingObserver(bluetoothDevice) { + @Override + public void bonding() { + //subscriber.onNext(new HashMap()); + } - @Override - public void bonded() { - subscriber.onComplete(); - } + @Override + public void bonded() { + subscriber.onComplete(); + } - @Override - public void bondNone() { - // BIG NOTE, don't produce error, sometimes android produces bond none before bonded or bonding event - // subscriber.onError(new Throwable("Bonding failed for unknown reason")); - } - }; - bondingManager.addObserver(observer[0]); - break; - } - case BluetoothDevice.BOND_BONDED: { - subscriber.onComplete(); - break; - } + @Override + public void bondNone() { + // BIG NOTE, don't produce error, sometimes android produces bond none before bonded or bonding event + // subscriber.onError(new Throwable("Bonding failed for unknown reason")); + } + }; + bondingManager.addObserver(observer[0]); + break; + } + case BluetoothDevice.BOND_BONDED: { + subscriber.onComplete(); + break; } - }else{ - subscriber.onError(new BleDisconnected()); } + } else { + subscriber.onError(new BleDisconnected()); } - }).doFinally(new Action() { - @Override - public void run() { - bondingManager.removeObserver(observer[0]); - } - }); + }).doFinally(() -> bondingManager.removeObserver(observer[0])); } @Override - public boolean isAuthenticated(){ + public boolean isAuthenticated() { return bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED; } @@ -218,10 +224,8 @@ public boolean clearGattCache() { public Single readRssiValue() { final SingleEmitter[] observer = new SingleEmitter[1]; return Single.create( - new SingleOnSubscribe() { - @Override - public void subscribe(SingleEmitter subscriber) throws Exception { - if( getSessionState() == DeviceSessionState.SESSION_OPEN ){ + (SingleOnSubscribe) subscriber -> { + if (getSessionState() == DeviceSessionState.SESSION_OPEN) { synchronized (gattMutex) { if (gatt != null) { if (gatt.readRemoteRssi()) { @@ -237,27 +241,21 @@ public void subscribe(SingleEmitter subscriber) throws Exception { } } subscriber.tryOnError(new BleDisconnected()); - } - }).doFinally(new Action() { - @Override - public void run() { - rssiObservers.remove(observer[0]); - } - }); + }).doFinally(() -> rssiObservers.remove(observer[0])); } - public boolean sendNextAttributeOperation(AttributeOperation operation) throws Throwable { + private boolean sendNextAttributeOperation(AttributeOperation operation) throws Throwable { BluetoothGattCharacteristic characteristic = operation.getCharacteristic(); synchronized (getGattMutex()) { - if(gatt != null) { + if (gatt != null) { switch (operation.getAttributeOperation()) { case CHARACTERISTIC_READ: { return getGatt().readCharacteristic(characteristic); } case CHARACTERISTIC_WRITE: { - if( operation.isWithResponse() && (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0 ){ + if (operation.isWithResponse() && (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) { characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); - }else if((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0 ){ + } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0) { characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); } characteristic.setValue(operation.getData()); @@ -278,11 +276,11 @@ public boolean sendNextAttributeOperation(AttributeOperation operation) throws T } break; } - default:{ + default: { throw new BleNotSupported("not supported"); } } - }else{ + } else { throw new BleGattNotInitialized("Attribute operation tryed while gatt is uninitialized"); } } @@ -291,24 +289,14 @@ public boolean sendNextAttributeOperation(AttributeOperation operation) throws T @Override public void gattClientRequestStopScanning() { - BleLogger.d(TAG,"GATT client request stop scanning"); - handler.post(new Runnable() { - @Override - public void run() { - bleScanCallback.stopScan(); - } - }); + BleLogger.d(TAG, "GATT client request stop scanning"); + handler.post(() -> bleScanCallback.stopScan()); } @Override public void gattClientResumeScanning() { - BleLogger.d(TAG,"GATT client request continue scanning"); - handler.post(new Runnable() { - @Override - public void run() { - bleScanCallback.startScan(); - } - }); + BleLogger.d(TAG, "GATT client request continue scanning"); + handler.post(() -> bleScanCallback.startScan()); } @Override @@ -316,27 +304,21 @@ public int transportQueueSize() { return attOperations.size(); } - @Override - public void discoverServices() { + void handleDisconnection() { // do nothing - } - - @Override - public void handleDisconnection() { - // do nothing - BleLogger.d(TAG,"disconnected"); + BleLogger.d(TAG, "disconnected"); advertisementContent.resetAdvertisementData(); attOperations.clear(); - for(BleGattBase gattclient : clients){ + for (BleGattBase gattclient : clients) { gattclient.reset(); } RxUtils.postDisconnectedAndClearList(servicesSubscriberAtomicList); RxUtils.postDisconnectedAndClearList(rssiObservers); - for( Disposable subscription : subscriptions ){ + for (Disposable subscription : subscriptions) { subscription.dispose(); } subscriptions.clear(); - if( serviceDiscovery != null ) { + if (serviceDiscovery != null) { serviceDiscovery.dispose(); serviceDiscovery = null; } @@ -344,18 +326,18 @@ public void handleDisconnection() { // GATT @Override - public void transmitMessages(BleGattBase gattclient, UUID serviceUuid, UUID characteristicUuid, List packets, boolean withResponse) throws Exception{ + public void transmitMessages(BleGattBase gattclient, UUID serviceUuid, UUID characteristicUuid, List packets, boolean withResponse) throws Exception { // note most likely this comes from a different thread - for(byte[] packet : packets){ - transmitMessage(gattclient,serviceUuid,characteristicUuid,packet,withResponse); + for (byte[] packet : packets) { + transmitMessage(gattclient, serviceUuid, characteristicUuid, packet, withResponse); } } @Override public void transmitMessage(BleGattBase gattclient, UUID serviceUuid, UUID characteristicUuid, byte[] packet, boolean withResponse) throws Exception { // note most likely this comes from a different thread - synchronized (gattMutex){ - if(gatt != null) { + synchronized (gattMutex) { + if (gatt != null) { for (BluetoothGattService service : gatt.getServices()) { if (service.getUuid().equals(serviceUuid)) { for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { @@ -428,13 +410,11 @@ public void setCharacteristicNotify(BleGattBase gattclient, UUID serviceUuid, UU } @Override - public Single> monitorServicesDiscovered(final boolean checkConnection){ + public Single> monitorServicesDiscovered(final boolean checkConnection) { final SingleEmitter>[] observer = new SingleEmitter[1]; return Single.create( - new SingleOnSubscribe>() { - @Override - public void subscribe(SingleEmitter> subscriber) { - if(checkConnection && getSessionState() != BleDeviceSession.DeviceSessionState.SESSION_OPEN){ + (SingleOnSubscribe>) subscriber -> { + if (checkConnection && getSessionState() != DeviceSessionState.SESSION_OPEN) { subscriber.tryOnError(new BleDisconnected()); } else { observer[0] = subscriber; @@ -450,23 +430,17 @@ public void subscribe(SingleEmitter> subscriber) { } } } - } - }).doFinally(new Action() { - @Override - public void run() { - servicesSubscriberAtomicList.remove(observer[0]); - } - }); + }).doFinally(() -> servicesSubscriberAtomicList.remove(observer[0])); } @Override - public boolean isConnected(){ + public boolean isConnected() { return getSessionState() == DeviceSessionState.SESSION_OPEN; } - boolean isAuthenticationNeeded(){ + boolean isAuthenticationNeeded() { synchronized (gattMutex) { - if(gatt != null) { + if (gatt != null) { for (BluetoothGattService service : gatt.getServices()) { final BleGattBase client = fetchClient(service.getUuid()); if (client != null && client.isEncryptionRequired()) { @@ -478,7 +452,7 @@ boolean isAuthenticationNeeded(){ return false; } - void handleServicesDiscovered(){ + void handleServicesDiscovered() { List operations = new ArrayList<>(); final List serviceUuids = new ArrayList<>(); synchronized (gattMutex) { @@ -493,59 +467,51 @@ void handleServicesDiscovered(){ } } RxUtils.emitNext(servicesSubscriberAtomicList, - new RxUtils.Emitter>>() { - @Override - public void item(SingleEmitter> object) { + object -> { List services = new ArrayList<>(serviceUuids); object.onSuccess(services); - } - }); + }); Collections.sort(operations); this.attOperations.clear(); this.attOperations.addAll(operations); } - private void handleServiceDiscovered(List operations, BluetoothGattService service){ + private void handleServiceDiscovered(List operations, BluetoothGattService service) { final BleGattBase client = fetchClient(service.getUuid()); BleLogger.d(TAG, " SERVICE: " + service.getUuid().toString()); if (client != null) { - client.setServiceDiscovered(true,service.getUuid()); - for( BluetoothGattCharacteristic characteristic : service.getCharacteristics() ) { - BleLogger.d(TAG," CHARACTERISTIC: "+ characteristic.getUuid().toString() + " PROPERTIES: " + characteristic.getProperties()); + client.setServiceDiscovered(true, service.getUuid()); + for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { + BleLogger.d(TAG, " CHARACTERISTIC: " + characteristic.getUuid().toString() + " PROPERTIES: " + characteristic.getProperties()); client.processCharacteristicDiscovered(characteristic.getUuid(), characteristic.getProperties()); if (client.containsNotifyCharacteristic(characteristic.getUuid()) && - ((characteristic.getProperties() & BleGattBase.PROPERTY_NOTIFY) != 0 || - (characteristic.getProperties() & BleGattBase.PROPERTY_INDICATE) != 0 ) && - client.isAutomatic(characteristic.getUuid())) { - AttributeOperation operation = new AttributeOperation(AttributeOperation.AttributeOperationCommand.DESCRIPTOR_WRITE, characteristic,true); + ((characteristic.getProperties() & BleGattBase.PROPERTY_NOTIFY) != 0 || + (characteristic.getProperties() & BleGattBase.PROPERTY_INDICATE) != 0) && + client.isAutomatic(characteristic.getUuid())) { + AttributeOperation operation = new AttributeOperation(AttributeOperation.AttributeOperationCommand.DESCRIPTOR_WRITE, characteristic, true); operation.setIsPartOfPrimaryService(client.isPrimaryService()); operations.add(operation); } if (client.containsCharacteristicRead(characteristic.getUuid()) && - (characteristic.getProperties() & BleGattBase.PROPERTY_READ) != 0 && - client.isAutomaticRead(characteristic.getUuid())) { + (characteristic.getProperties() & BleGattBase.PROPERTY_READ) != 0 && + client.isAutomaticRead(characteristic.getUuid())) { AttributeOperation operation = new AttributeOperation(AttributeOperation.AttributeOperationCommand.CHARACTERISTIC_READ, characteristic); operation.setIsPartOfPrimaryService(client.isPrimaryService()); operations.add(operation); } } } else { - BleLogger.d(TAG,"No client found for SERVICE: " + service.getUuid().toString() + " chrs: " + service.getCharacteristics().size()); + BleLogger.d(TAG, "No client found for SERVICE: " + service.getUuid().toString() + " chrs: " + service.getCharacteristics().size()); } } void handleCharacteristicWrite(final BluetoothGattService service, final BluetoothGattCharacteristic characteristic, int status) { - logIfError("handleCharacteristicWrite uuid: " + characteristic.getUuid().toString(),status); + logIfError("handleCharacteristicWrite uuid: " + characteristic.getUuid().toString(), status); switch (status) { case BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION: case BleGattBase.ATT_INSUFFICIENT_ENCRYPTION: { BleLogger.e(TAG, "Attribute operation write failed due the reason: " + status); - startAuthentication(new Action() { - @Override - public void run() { - handleAuthenticationComplete(); - } - }); + startAuthentication(this::handleAuthenticationComplete); } default: { final BleGattBase client = fetchClient(service.getUuid()); @@ -556,8 +522,8 @@ public void run() { client.processServiceDataWritten(characteristic.getUuid(), status); } } - if( status != BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION && - status != BleGattBase.ATT_INSUFFICIENT_ENCRYPTION ) { + if (status != BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION && + status != BleGattBase.ATT_INSUFFICIENT_ENCRYPTION) { processNextAttributeOperation(true); } break; @@ -566,21 +532,16 @@ public void run() { } void handleCharacteristicRead(final BluetoothGattService service, final BluetoothGattCharacteristic characteristic, byte[] value, int status) { - logIfError("handleCharacteristicRead uuid: " + characteristic.getUuid().toString(),status); - switch (status){ + logIfError("handleCharacteristicRead uuid: " + characteristic.getUuid().toString(), status); + switch (status) { case BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION: - case BleGattBase.ATT_INSUFFICIENT_ENCRYPTION:{ + case BleGattBase.ATT_INSUFFICIENT_ENCRYPTION: { BleLogger.e(TAG, "Attribute operation read failed due the reason: " + status); - startAuthentication(new Action() { - @Override - public void run() { - handleAuthenticationComplete(); - } - }); + startAuthentication(this::handleAuthenticationComplete); } default: { - if( status != BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION && - status != BleGattBase.ATT_INSUFFICIENT_ENCRYPTION ) { + if (status != BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION && + status != BleGattBase.ATT_INSUFFICIENT_ENCRYPTION) { processNextAttributeOperation(true); } final BleGattBase client = fetchClient(service.getUuid()); @@ -601,7 +562,7 @@ void handleCharacteristicValueUpdated(final BluetoothGattService service, final client.processServiceData(characteristic.getUuid(), value, BleGattBase.ATT_SUCCESS, true); } } else { - BleLogger.e(TAG,"Unhandled notification received"); + BleLogger.e(TAG, "Unhandled notification received"); } } @@ -611,26 +572,21 @@ void handleDescriptorRead(BluetoothGattDescriptor descriptor, byte[] value, int } void handleDescriptorWrite(final BluetoothGattService service, final BluetoothGattCharacteristic characteristic, final BluetoothGattDescriptor descriptor, byte[] value, int status) { - BleLogger.d(TAG,"onDescriptorWrite uuid: " + characteristic.getUuid().toString() + " status: " + status); + BleLogger.d(TAG, "onDescriptorWrite uuid: " + characteristic.getUuid().toString() + " status: " + status); switch (status) { case BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION: case BleGattBase.ATT_INSUFFICIENT_ENCRYPTION: { BleLogger.e(TAG, "Attribute operation descriptor write failed due the reason: " + status); - startAuthentication(new Action() { - @Override - public void run() { - handleAuthenticationComplete(); - } - }); + startAuthentication(this::handleAuthenticationComplete); } default: { - if( status != BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION && - status != BleGattBase.ATT_INSUFFICIENT_ENCRYPTION ) { + if (status != BleGattBase.ATT_INSUFFICIENT_AUTHENTICATION && + status != BleGattBase.ATT_INSUFFICIENT_ENCRYPTION) { processNextAttributeOperation(true); } byte[] disable = new byte[]{0x00, 0x00}; - boolean activated = !Arrays.equals(disable,value); - if(status != BleGattBase.ATT_SUCCESS){ + boolean activated = !Arrays.equals(disable, value); + if (status != BleGattBase.ATT_SUCCESS) { activated = false; } final BleGattBase client = fetchClient(service.getUuid()); @@ -645,28 +601,28 @@ public void run() { } /** - * @param mtu att mtu size + * @param mtu att mtu size * @param status @see BleGattBase error codes */ void handleMtuChanged(int mtu, int status) { BleLogger.d(TAG, "handleMtuChanged status: " + status + " mtu: " + mtu); - if(status == BleGattBase.ATT_SUCCESS){ - for( BleGattBase gattclient : clients ){ + if (status == BleGattBase.ATT_SUCCESS) { + for (BleGattBase gattclient : clients) { gattclient.setMtuSize(mtu); } } } - void handleAuthenticationComplete(){ + void handleAuthenticationComplete() { processNextAttributeOperation(false); - for(BleGattBase gattClient : clients){ + for (BleGattBase gattClient : clients) { gattClient.authenticationCompleted(); } } - private void handleAuthenticationFailed(Throwable e){ + private void handleAuthenticationFailed(Throwable e) { processNextAttributeOperation(false); - for(BleGattBase gattClient : clients){ + for (BleGattBase gattClient : clients) { gattClient.authenticationFailed(e); } } @@ -679,7 +635,7 @@ void processNextAttributeOperation(boolean remove) { } if (attOperations.size() != 0) { AttributeOperation operation = attOperations.peek(); - if(BuildConfig.DEBUG) { + if (BuildConfig.DEBUG) { BleLogger.d(TAG, "send next: " + operation.getCharacteristic().getUuid() + " op: " + operation.getAttributeOperation().toString()); } try { @@ -704,18 +660,11 @@ void processNextAttributeOperation(boolean remove) { } } - @Override - public void startAuthentication(Action complete){ + void startAuthentication(Action complete) { + // try next att operation anyway subscriptions.add(authenticate().toObservable().delaySubscription(500, TimeUnit.MILLISECONDS).ignoreElements(). observeOn(AndroidSchedulers.from(context.getMainLooper())).subscribe( - complete, - new Consumer() { - @Override - public void accept(Throwable throwable) { - // try next att operation anyway - handleAuthenticationFailed(throwable); - - } - })); + complete, + this::handleAuthenticationFailed)); } -} +} \ No newline at end of file diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDGattCallback.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDGattCallback.java index ed37526a..afaa4428 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDGattCallback.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDGattCallback.java @@ -1,6 +1,5 @@ package com.androidcommunications.polar.enpoints.ble.bluedroid.host; -import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; @@ -11,17 +10,14 @@ import com.androidcommunications.polar.api.ble.BleLogger; import com.androidcommunications.polar.common.ble.RxUtils; -import com.androidcommunications.polar.enpoints.ble.common.connection.ConnectionHandler; +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection.ConnectionHandler; import java.util.concurrent.TimeUnit; -import io.reactivex.Observable; -import io.reactivex.Scheduler; -import io.reactivex.SingleEmitter; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.functions.Action; -import io.reactivex.functions.Consumer; -import io.reactivex.schedulers.Schedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Scheduler; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.schedulers.Schedulers; class BDGattCallback extends BluetoothGattCallback { @@ -46,34 +42,19 @@ void setPolarMaxMtu(int mtu) { public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) { final BDDeviceSessionImpl smartPolarDeviceSession = sessions.getSession(gatt); BleLogger.d(TAG, "GATT state changed device newState: " + newState + " status: " + status); - if( smartPolarDeviceSession != null ) { + if (smartPolarDeviceSession != null) { if (newState == BluetoothGatt.STATE_CONNECTED) { if (status == BluetoothGatt.GATT_SUCCESS) { connectionHandler.deviceConnected(smartPolarDeviceSession); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { gatt.setPreferredPhy(BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_OPTION_NO_PREFERRED); } - if(smartPolarDeviceSession.isAuthenticated()) { + if (smartPolarDeviceSession.isAuthenticated()) { smartPolarDeviceSession.getSubscriptions().add(Observable.timer(600, TimeUnit.MILLISECONDS, Schedulers.newThread()).observeOn(scheduler).subscribe( - new Consumer() { - @Override - public void accept(Long aLong) { - - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) { - BleLogger.e(TAG, "Wait encryption start failed: " + throwable.getLocalizedMessage()); - } - }, - new Action() { - @SuppressLint("NewApi") - @Override - public void run() { - startDiscovery(smartPolarDeviceSession, gatt); - } - })); + aLong -> { + }, + throwable -> BleLogger.e(TAG, "Wait encryption start failed: " + throwable.getLocalizedMessage()), + () -> startDiscovery(smartPolarDeviceSession, gatt))); } else { startDiscovery(smartPolarDeviceSession, gatt); } @@ -84,8 +65,8 @@ public void run() { connectionHandler.deviceDisconnected(smartPolarDeviceSession); } } else { - BleLogger.e(TAG,"Dead gatt object received"); - if(gatt!=null) { + BleLogger.e(TAG, "Dead gatt object received"); + if (gatt != null) { gatt.close(); } } @@ -95,7 +76,7 @@ public void run() { public void onServicesDiscovered(final BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); final BDDeviceSessionImpl bdDeviceSession = sessions.getSession(gatt); - if(bdDeviceSession != null) { + if (bdDeviceSession != null) { if (bdDeviceSession.serviceDiscovery != null) { bdDeviceSession.serviceDiscovery.dispose(); bdDeviceSession.serviceDiscovery = null; @@ -107,7 +88,7 @@ public void onServicesDiscovered(final BluetoothGatt gatt, int status) { BleLogger.e(TAG, "service discovery failed: " + status); } } else { - BleLogger.e(TAG,"services discovered on non known gatt"); + BleLogger.e(TAG, "services discovered on non known gatt"); } } @@ -115,11 +96,11 @@ public void onServicesDiscovered(final BluetoothGatt gatt, int status) { public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); final BDDeviceSessionImpl session = sessions.getSession(gatt); - if( session != null ) { - session.handleCharacteristicRead(characteristic.getService(),characteristic,characteristic.getValue(),status); + if (session != null) { + session.handleCharacteristicRead(characteristic.getService(), characteristic, characteristic.getValue(), status); } else { - BleLogger.e(TAG,"Dead gatt event?"); - if(gatt!=null) { + BleLogger.e(TAG, "Dead gatt event?"); + if (gatt != null) { gatt.close(); } } @@ -130,10 +111,10 @@ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristi super.onCharacteristicWrite(gatt, characteristic, status); final BDDeviceSessionImpl session = sessions.getSession(gatt); if (session != null) { - session.handleCharacteristicWrite(characteristic.getService(),characteristic,status); + session.handleCharacteristicWrite(characteristic.getService(), characteristic, status); } else { - BleLogger.e(TAG,"Dead gatt event?"); - if(gatt!=null) { + BleLogger.e(TAG, "Dead gatt event?"); + if (gatt != null) { gatt.close(); } } @@ -144,10 +125,10 @@ public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattChara super.onCharacteristicChanged(gatt, characteristic); final BDDeviceSessionImpl session = sessions.getSession(gatt); if (session != null) { - session.handleCharacteristicValueUpdated(characteristic.getService(),characteristic,characteristic.getValue()); + session.handleCharacteristicValueUpdated(characteristic.getService(), characteristic, characteristic.getValue()); } else { - BleLogger.e(TAG,"Dead gatt event?"); - if(gatt!=null) { + BleLogger.e(TAG, "Dead gatt event?"); + if (gatt != null) { gatt.close(); } } @@ -158,10 +139,10 @@ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descrip super.onDescriptorRead(gatt, descriptor, status); final BDDeviceSessionImpl session = sessions.getSession(gatt); if (session != null) { - session.handleDescriptorRead(descriptor,descriptor.getValue(),status); + session.handleDescriptorRead(descriptor, descriptor.getValue(), status); } else { - BleLogger.e(TAG,"Dead gatt event?"); - if(gatt!=null) { + BleLogger.e(TAG, "Dead gatt event?"); + if (gatt != null) { gatt.close(); } } @@ -172,31 +153,26 @@ public void onDescriptorWrite(BluetoothGatt gatt, final BluetoothGattDescriptor super.onDescriptorWrite(gatt, descriptor, status); final BDDeviceSessionImpl session = sessions.getSession(gatt); if (session != null) { - session.handleDescriptorWrite(descriptor.getCharacteristic().getService(),descriptor.getCharacteristic(),descriptor,descriptor.getValue(),status); + session.handleDescriptorWrite(descriptor.getCharacteristic().getService(), descriptor.getCharacteristic(), descriptor, descriptor.getValue(), status); } else { - BleLogger.e(TAG,"Dead gatt event?"); - if(gatt!=null) { + BleLogger.e(TAG, "Dead gatt event?"); + if (gatt != null) { gatt.close(); } } } @Override - public void onReadRemoteRssi (final BluetoothGatt gatt, final int rssi, int status) { + public void onReadRemoteRssi(final BluetoothGatt gatt, final int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); BleLogger.d(TAG, "onReadRemoteRssi status: " + status); final BDDeviceSessionImpl session = sessions.getSession(gatt); if (session != null) { RxUtils.emitNext(session.getRssiObservers(), - new RxUtils.Emitter>() { - @Override - public void item(SingleEmitter object) { - object.onSuccess(rssi); - } - }); + object -> object.onSuccess(rssi)); } else { - BleLogger.e(TAG,"Dead gatt event?"); - if(gatt!=null) { + BleLogger.e(TAG, "Dead gatt event?"); + if (gatt != null) { gatt.close(); } } @@ -207,24 +183,19 @@ public void onMtuChanged(final BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); BleLogger.d(TAG, "onMtuChanged status: " + status); final BDDeviceSessionImpl smartDeviceSession = sessions.getSession(gatt); - if(smartDeviceSession!=null) { + if (smartDeviceSession != null) { smartDeviceSession.handleMtuChanged(mtu, status); if (smartDeviceSession.isAuthenticationNeeded() && !smartDeviceSession.isAuthenticated()) { BleLogger.d(TAG, "Services discovered authentication is needed"); - smartDeviceSession.startAuthentication(new Action() { - @Override - public void run() { - // first mtu exchange - smartDeviceSession.handleAuthenticationComplete(); - } - }); + // first mtu exchange + smartDeviceSession.startAuthentication(smartDeviceSession::handleAuthenticationComplete); } else { BleLogger.d(TAG, "Services discovered authentication is not needed"); smartDeviceSession.processNextAttributeOperation(false); } } else { - BleLogger.e(TAG,"Dead gatt event?"); - if(gatt!=null) { + BleLogger.e(TAG, "Dead gatt event?"); + if (gatt != null) { gatt.close(); } } @@ -232,14 +203,14 @@ public void run() { @Override public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { - super.onPhyUpdate(gatt,txPhy,rxPhy,status); - BleLogger.d(TAG," phy updated tx: " + txPhy + " rx: " + rxPhy + " status: " + status); + super.onPhyUpdate(gatt, txPhy, rxPhy, status); + BleLogger.d(TAG, " phy updated tx: " + txPhy + " rx: " + rxPhy + " status: " + status); } @Override public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { - super.onPhyRead(gatt,txPhy,rxPhy,status); - BleLogger.d(TAG," phy read tx: " + txPhy + " rx: " + rxPhy + " status: " + status); + super.onPhyRead(gatt, txPhy, rxPhy, status); + BleLogger.d(TAG, " phy read tx: " + txPhy + " rx: " + rxPhy + " status: " + status); } private void startDiscovery(BDDeviceSessionImpl smartDeviceSession, final BluetoothGatt gatt) { @@ -248,23 +219,9 @@ private void startDiscovery(BDDeviceSessionImpl smartDeviceSession, final Blueto smartDeviceSession.serviceDiscovery.dispose(); } smartDeviceSession.serviceDiscovery = Observable.timer(10, TimeUnit.SECONDS, Schedulers.newThread()).observeOn(scheduler).subscribe( - new Consumer() { - @Override - public void accept(Long aLong) { - - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) { - BleLogger.e(TAG, "service discovery timer failed: " + throwable.getLocalizedMessage()); - } - }, - new Action() { - @Override - public void run() { - onServicesDiscovered(gatt, 0); - } - }); + aLong -> { + }, + throwable -> BleLogger.e(TAG, "service discovery timer failed: " + throwable.getLocalizedMessage()), + () -> onServicesDiscovered(gatt, 0)); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDPowerListener.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDPowerListener.java index c881234a..d78a8fe2 100644 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDPowerListener.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDPowerListener.java @@ -8,8 +8,9 @@ class BDPowerListener { - interface BlePowerState{ + interface BlePowerState { void blePoweredOff(); + void blePoweredOn(); } @@ -24,8 +25,8 @@ interface BlePowerState{ this.powerState = powerState; } - void stopBroadcastReceiver(){ - if(receiver != null) { + void stopBroadcastReceiver() { + if (receiver != null) { context.unregisterReceiver(receiver); receiver = null; } @@ -38,7 +39,7 @@ public void onReceive(Context context, Intent intent) { if (action != null && action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) { powerState.blePoweredOff(); - }else if(bluetoothAdapter.getState() == BluetoothAdapter.STATE_ON){ + } else if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { powerState.blePoweredOn(); } } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDScanCallback.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDScanCallback.java index 5525d1b0..05b79533 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDScanCallback.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/BDScanCallback.java @@ -10,19 +10,19 @@ import android.bluetooth.le.ScanSettings; import android.content.Context; import android.os.Build; + import com.androidcommunications.polar.api.ble.BleLogger; import com.androidcommunications.polar.common.ble.BleUtils; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; -import io.reactivex.Observable; -import io.reactivex.Scheduler; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.functions.Action; -import io.reactivex.functions.Consumer; -import io.reactivex.schedulers.Schedulers; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Scheduler; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; class BDScanCallback extends ScanCallback { @@ -54,14 +54,16 @@ private enum ScannerState { private Scheduler delayScheduler; private Disposable delaySubscription; private Disposable opportunisticScanTimer; - private boolean opportunistic=true; + private boolean opportunistic = true; // scan window limit, for android's "is scanning too frequently" private final static int SCAN_WINDOW_LIMIT = 30000; - public interface BDScanCallbackInterface{ + public interface BDScanCallbackInterface { void deviceDiscovered(final BluetoothDevice device, int rssi, byte[] scanRecord, BleUtils.EVENT_TYPE type); + void scanStartError(int error); + boolean isScanningNeeded(); } @@ -83,39 +85,39 @@ void setLowPowerEnabled(boolean lowPowerEnabled) { this.lowPowerEnabled = lowPowerEnabled; } - void setScanFilters(final List filters){ + void setScanFilters(final List filters) { stopScan(); this.filters = filters; startScan(); } - void clientAdded(){ + void clientAdded() { commandState(ScanAction.CLIENT_START_SCAN); } - void clientRemoved(){ + void clientRemoved() { commandState(ScanAction.CLIENT_REMOVED); } - void stopScan(){ + void stopScan() { commandState(ScanAction.ADMIN_STOP_SCAN); } - void startScan(){ + void startScan() { commandState(ScanAction.ADMIN_START_SCAN); } - void powerOn(){ + void powerOn() { commandState(ScanAction.BLE_POWER_ON); } - void powerOff(){ + void powerOff() { commandState(ScanAction.BLE_POWER_OFF); } - private void commandState(ScanAction action){ + private void commandState(ScanAction action) { BleLogger.d(TAG, "commandState state:" + state.toString() + " action: " + action.toString()); - switch (state){ + switch (state) { case IDLE: { scannerIdleState(action); break; @@ -131,17 +133,17 @@ private void commandState(ScanAction action){ } } - private void changeState(ScannerState newState){ + private void changeState(ScannerState newState) { commandState(ScanAction.EXIT); this.state = newState; commandState(ScanAction.ENTRY); } - private void scannerIdleState(ScanAction action){ - switch (action){ - case ENTRY:{ - if(bluetoothAdapter != null && bluetoothAdapter.isEnabled()){ - if(scanCallbackInterface.isScanningNeeded()){ + private void scannerIdleState(ScanAction action) { + switch (action) { + case ENTRY: { + if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) { + if (scanCallbackInterface.isScanningNeeded()) { changeState(ScannerState.SCANNING); } } @@ -152,22 +154,22 @@ private void scannerIdleState(ScanAction action){ case BLE_POWER_OFF: { break; } - case CLIENT_START_SCAN:{ - if(bluetoothAdapter != null && bluetoothAdapter.isEnabled()){ - if(scanCallbackInterface.isScanningNeeded()) { + case CLIENT_START_SCAN: { + if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) { + if (scanCallbackInterface.isScanningNeeded()) { changeState(ScannerState.SCANNING); } } else { - BleLogger.d(TAG,"Skipped scan start, because of ble power off"); + BleLogger.d(TAG, "Skipped scan start, because of ble power off"); } break; } - case ADMIN_STOP_SCAN:{ + case ADMIN_STOP_SCAN: { changeState(ScannerState.STOPPED); break; } - case BLE_POWER_ON:{ - if(scanCallbackInterface.isScanningNeeded()){ + case BLE_POWER_ON: { + if (scanCallbackInterface.isScanningNeeded()) { // if there is atleast one client waiting changeState(ScannerState.SCANNING); } @@ -176,87 +178,87 @@ private void scannerIdleState(ScanAction action){ } } - private void scannerAdminState(ScanAction action){ + private void scannerAdminState(ScanAction action) { // forced stopped state - switch (action){ - case ENTRY:{ + switch (action) { + case ENTRY: { adminStops = 1; break; } - case EXIT:{ + case EXIT: { adminStops = 0; break; } - case ADMIN_START_SCAN:{ + case ADMIN_START_SCAN: { // go through idle state back to scanning, if needed --adminStops; - if( adminStops <= 0 ) { + if (adminStops <= 0) { changeState(ScannerState.IDLE); } else { - BleLogger.d(TAG,"Waiting admins to call start c: " + adminStops); + BleLogger.d(TAG, "Waiting admins to call start c: " + adminStops); } break; } - case ADMIN_STOP_SCAN:{ + case ADMIN_STOP_SCAN: { ++adminStops; break; } - case BLE_POWER_OFF:{ + case BLE_POWER_OFF: { changeState(ScannerState.IDLE); break; } case CLIENT_REMOVED: case CLIENT_START_SCAN: - case BLE_POWER_ON:{ + case BLE_POWER_ON: { // do nothing break; } } } - private void scannerScanningState(ScanAction action){ - switch (action){ - case ENTRY:{ + private void scannerScanningState(ScanAction action) { + switch (action) { + case ENTRY: { // start scanning startScanning(); break; } - case EXIT:{ + case EXIT: { // stop scanning stopScanning(); - if(opportunisticScanTimer != null){ + if (opportunisticScanTimer != null) { opportunisticScanTimer.dispose(); opportunisticScanTimer = null; } - if(timer != null){ + if (timer != null) { timer.dispose(); timer = null; } break; } - case CLIENT_START_SCAN:{ + case CLIENT_START_SCAN: { // do nothing break; } - case CLIENT_REMOVED:{ - if(!scanCallbackInterface.isScanningNeeded()){ + case CLIENT_REMOVED: { + if (!scanCallbackInterface.isScanningNeeded()) { // scanning is not needed anymore changeState(ScannerState.IDLE); } break; } - case ADMIN_STOP_SCAN:{ + case ADMIN_STOP_SCAN: { changeState(ScannerState.STOPPED); break; } - case BLE_POWER_OFF:{ + case BLE_POWER_OFF: { changeState(ScannerState.IDLE); break; } case ADMIN_START_SCAN: // skip break; - case BLE_POWER_ON:{ + case BLE_POWER_ON: { // should not happen BleLogger.e(TAG, "INCORRECT event received in scanning state: " + action); break; @@ -271,7 +273,7 @@ public void onScanResult(int callbackType, ScanResult result) { @Override public void onBatchScanResults(List results) { - for(ScanResult result : results) { + for (ScanResult result : results) { scanCallbackInterface.deviceDiscovered(result.getDevice(), result.getRssi(), result.getScanRecord() != null ? result.getScanRecord().getBytes() : new byte[]{}, fetchAdvType(result)); } } @@ -284,10 +286,10 @@ public void onScanFailed(int errorCode) { @SuppressLint("CheckResult") private void startScanning() { - if( android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ){ - if(scanPool.size()!=0){ + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (scanPool.size() != 0) { long elapsed = System.currentTimeMillis() - scanPool.get(0); - if( scanPool.size() > 3 && elapsed < SCAN_WINDOW_LIMIT ) { + if (scanPool.size() > 3 && elapsed < SCAN_WINDOW_LIMIT) { long sift = (SCAN_WINDOW_LIMIT - elapsed) + 200; BleLogger.d(TAG, "Prevent scanning too frequently delay: " + sift + "ms" + " elapsed: " + elapsed + "ms"); if (delaySubscription != null) { @@ -295,37 +297,26 @@ private void startScanning() { delaySubscription = null; } delaySubscription = Observable.timer(sift, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io()).observeOn(delayScheduler).subscribe( - new Consumer() { - @Override - public void accept(Long aLong) { + aLong -> { // do nothing - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) { - BleLogger.e(TAG, "timer failed: " + throwable.getLocalizedMessage()); - } - }, - new Action() { - @Override - public void run() { + }, + throwable -> BleLogger.e(TAG, "timer failed: " + throwable.getLocalizedMessage()), + () -> { BleLogger.d(TAG, "delayed scan starting"); if (scanPool.size() != 0) scanPool.remove(0); startLScan(); - } - }); + }); return; } } - BleLogger.d(TAG,"timestamps left: " + scanPool.size()); + BleLogger.d(TAG, "timestamps left: " + scanPool.size()); startLScan(); } else { startLScan(); } } - private BleUtils.EVENT_TYPE fetchAdvType(ScanResult result){ + private BleUtils.EVENT_TYPE fetchAdvType(ScanResult result) { BleUtils.EVENT_TYPE type = BleUtils.EVENT_TYPE.ADV_IND; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !result.isConnectable()) { type = BleUtils.EVENT_TYPE.ADV_NONCONN_IND; @@ -334,7 +325,7 @@ private BleUtils.EVENT_TYPE fetchAdvType(ScanResult result){ } @SuppressLint("NewApi") - private void startLScan(){ + private void startLScan() { BleLogger.d(TAG, "Scan started -->"); final ScanSettings scanSettings; if (!lowPowerEnabled) { @@ -344,31 +335,20 @@ private void startLScan(){ } try { callStartScanL(scanSettings); - if( android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && opportunistic) { - opportunisticScanTimer = Observable.interval(30,TimeUnit.MINUTES).subscribeOn(Schedulers.io()).observeOn(delayScheduler).subscribe( - new Consumer() { - @Override - public void accept(Long aLong) { - BleLogger.d(TAG,"RESTARTING scan to avoid opportunistic"); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && opportunistic) { + opportunisticScanTimer = Observable.interval(30, TimeUnit.MINUTES).subscribeOn(Schedulers.io()).observeOn(delayScheduler).subscribe( + aLong -> { + BleLogger.d(TAG, "RESTARTING scan to avoid opportunistic"); stopScanning(); callStartScanL(scanSettings); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) { - BleLogger.e(TAG,"TIMER failed: " + throwable.getLocalizedMessage()); - } - }, - new Action() { - @Override - public void run() { + }, + throwable -> BleLogger.e(TAG, "TIMER failed: " + throwable.getLocalizedMessage()), + () -> { // non produced } - } ); } - BleLogger.d(TAG,"Scan started <--"); + BleLogger.d(TAG, "Scan started <--"); } catch (NullPointerException ex) { BleLogger.e(TAG, "startScan did throw null pointer exception"); changeState(ScannerState.IDLE); @@ -376,38 +356,33 @@ public void run() { } @SuppressLint("NewApi") - private void callStartScanL(ScanSettings scanSettings){ + private void callStartScanL(ScanSettings scanSettings) { try { bluetoothAdapter.getBluetoothLeScanner().startScan(filters, scanSettings, this); - } catch (Exception e){ - BleLogger.e(TAG,"Failed to start scan e: " + e.getLocalizedMessage()); + } catch (Exception e) { + BleLogger.e(TAG, "Failed to start scan e: " + e.getLocalizedMessage()); changeState(ScannerState.IDLE); return; } if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - scanPool.removeIf(new Predicate() { - @Override - public boolean test(Long aLong) { - return (System.currentTimeMillis() - aLong) >= SCAN_WINDOW_LIMIT; - } - }); + scanPool.removeIf(aLong -> (System.currentTimeMillis() - aLong) >= SCAN_WINDOW_LIMIT); scanPool.add(System.currentTimeMillis()); } } @SuppressLint("CheckResult") - private void stopScanning(){ + private void stopScanning() { BleLogger.d(TAG, "Stop scanning"); - if( android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ){ - if(delaySubscription != null){ + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (delaySubscription != null) { delaySubscription.dispose(); delaySubscription = null; } } try { bluetoothAdapter.getBluetoothLeScanner().stopScan(this); - }catch (Exception ex){ - BleLogger.e(TAG,"stopScan did throw exception: " + ex.getLocalizedMessage()); + } catch (Exception ex) { + BleLogger.e(TAG, "stopScan did throw exception: " + ex.getLocalizedMessage()); } } -} +} \ No newline at end of file diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionHandler.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionHandler.java similarity index 53% rename from sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionHandler.java rename to sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionHandler.java index 1f3c1ff4..b1cf8e8e 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionHandler.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionHandler.java @@ -1,13 +1,12 @@ -package com.androidcommunications.polar.enpoints.ble.common.connection; +package com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection; import android.content.Context; import android.os.Handler; import com.androidcommunications.polar.api.ble.BleLogger; import com.androidcommunications.polar.api.ble.model.BleDeviceSession; -import com.androidcommunications.polar.common.ble.AtomicSet; import com.androidcommunications.polar.common.ble.BleUtils; -import com.androidcommunications.polar.enpoints.ble.common.BleDeviceSession2; +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.BDDeviceSessionImpl; import java.util.HashMap; @@ -19,7 +18,7 @@ public class ConnectionHandler { /** * Connection handler state's */ - private enum ConnectionHandlerState{ + private enum ConnectionHandlerState { FREE, CONNECTING } @@ -27,7 +26,7 @@ private enum ConnectionHandlerState{ /** * Connection handler current state action */ - private enum ConnectionHandlerAction{ + private enum ConnectionHandlerAction { ENTRY, EXIT, CONNECT_DEVICE, @@ -41,34 +40,37 @@ private enum ConnectionHandlerAction{ private ConnectionHandlerState state; private ScannerInterface scannerInterface; private ConnectionInterface connectionInterface; - private BleDeviceSession2 current; - private AtomicSet observers = new AtomicSet<>(); + private BDDeviceSessionImpl current; + private ConnectionHandlerObserver observer; private boolean automaticReconnection = true; private Handler stateHandler; - public ConnectionHandler(Context context, ConnectionInterface connectionInterface, - ScannerInterface scannerInterface){ + public ConnectionHandler(Context context, + ConnectionInterface connectionInterface, + ScannerInterface scannerInterface, + ConnectionHandlerObserver observer) { this.stateHandler = new Handler(context.getMainLooper()); this.scannerInterface = scannerInterface; this.connectionInterface = connectionInterface; this.state = ConnectionHandlerState.FREE; + this.observer = observer; } public void setAutomaticReconnection(boolean automaticReconnection) { this.automaticReconnection = automaticReconnection; } - public void advertisementHeadReceived(final BleDeviceSession2 bleDeviceSession){ + public void advertisementHeadReceived(final BDDeviceSessionImpl bleDeviceSession) { commandState(bleDeviceSession, ConnectionHandlerAction.ADVERTISEMENT_HEAD_RECEIVED); } - public void connectDevice(BleDeviceSession2 bleDeviceSession, boolean bluetoothEnabled){ - if(bluetoothEnabled) { + public void connectDevice(BDDeviceSessionImpl bleDeviceSession, boolean bluetoothEnabled) { + if (bluetoothEnabled) { commandState(bleDeviceSession, ConnectionHandlerAction.CONNECT_DEVICE); - }else{ - switch (bleDeviceSession.getSessionState()){ + } else { + switch (bleDeviceSession.getSessionState()) { case SESSION_CLOSED: - case SESSION_CLOSING:{ + case SESSION_CLOSING: { updateSessionState(bleDeviceSession, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); break; } @@ -76,97 +78,66 @@ public void connectDevice(BleDeviceSession2 bleDeviceSession, boolean bluetoothE } } - public void disconnectDevice(BleDeviceSession2 bleDeviceSession){ + public void disconnectDevice(BDDeviceSessionImpl bleDeviceSession) { commandState(bleDeviceSession, ConnectionHandlerAction.DISCONNECT_DEVICE); } - public void deviceConnected(final BleDeviceSession2 bleDeviceSession){ - stateHandler.post(new Runnable() { - @Override - public void run() { - commandState(bleDeviceSession, ConnectionHandlerAction.DEVICE_CONNECTED); - observers.accessAll(new AtomicSet.ObjectAccess() { - @Override - public void access(ConnectionHandlerObserver object) { - object.deviceConnected(bleDeviceSession); - } - }); - } + public void deviceConnected(final BDDeviceSessionImpl bleDeviceSession) { + stateHandler.post(() -> { + commandState(bleDeviceSession, ConnectionHandlerAction.DEVICE_CONNECTED); + observer.deviceConnected(bleDeviceSession); }); } - public void deviceDisconnected(final BleDeviceSession2 bleDeviceSession){ - stateHandler.post(new Runnable() { - @Override - public void run() { - commandState(bleDeviceSession, ConnectionHandlerAction.DEVICE_DISCONNECTED); - observers.accessAll(new AtomicSet.ObjectAccess() { - @Override - public void access(ConnectionHandlerObserver object) { - object.deviceDisconnected(bleDeviceSession); - } - }); - } + public void deviceDisconnected(final BDDeviceSessionImpl bleDeviceSession) { + stateHandler.post(() -> { + commandState(bleDeviceSession, ConnectionHandlerAction.DEVICE_DISCONNECTED); + observer.deviceDisconnected(bleDeviceSession); }); } - public void addObserver(ConnectionHandlerObserver connectionHandlerObserver){ - observers.add(connectionHandlerObserver); - } - - public void removeObserver(ConnectionHandlerObserver connectionHandlerObserver){ - observers.remove(connectionHandlerObserver); - } - /** * INTERNAL */ - private void commandState(BleDeviceSession2 bleDeviceSession, ConnectionHandlerAction action){ - switch (state){ - case FREE: - { - free(bleDeviceSession,action); + private void commandState(BDDeviceSessionImpl bleDeviceSession, ConnectionHandlerAction action) { + switch (state) { + case FREE: { + free(bleDeviceSession, action); break; } - case CONNECTING: - { + case CONNECTING: { BleLogger.d(TAG, "state: " + state.toString() + " action: " + action.toString()); - connecting(bleDeviceSession,action); + connecting(bleDeviceSession, action); break; } } } - private void changeState(BleDeviceSession2 bleDeviceSession, ConnectionHandlerState newState){ + private void changeState(BDDeviceSessionImpl bleDeviceSession, ConnectionHandlerState newState) { commandState(bleDeviceSession, ConnectionHandlerAction.EXIT); this.state = newState; commandState(bleDeviceSession, ConnectionHandlerAction.ENTRY); } - private void updateSessionState(final BleDeviceSession2 bleDeviceSession, BleDeviceSession.DeviceSessionState newState){ + private void updateSessionState(final BDDeviceSessionImpl bleDeviceSession, BleDeviceSession.DeviceSessionState newState) { BleLogger.d(TAG, " Session update from: " + bleDeviceSession.getSessionState().toString() + " to: " + newState.toString()); bleDeviceSession.setSessionState(newState); - observers.accessAll(new AtomicSet.ObjectAccess() { - @Override - public void access(ConnectionHandlerObserver object) { - object.deviceSessionStateChanged(bleDeviceSession); - } - }); + observer.deviceSessionStateChanged(bleDeviceSession); } - private boolean containsRequiredUuids(final BleDeviceSession2 session){ - if( session.getConnectionUuids().size() != 0 ){ + private boolean containsRequiredUuids(final BDDeviceSessionImpl session) { + if (session.getConnectionUuids().size() != 0) { HashMap content = session.getAdvertisementContent().getAdvertisementData(); if (content.containsKey(BleUtils.AD_TYPE.GAP_ADTYPE_16BIT_MORE) || - content.containsKey(BleUtils.AD_TYPE.GAP_ADTYPE_16BIT_COMPLETE)) { + content.containsKey(BleUtils.AD_TYPE.GAP_ADTYPE_16BIT_COMPLETE)) { byte[] uuids = content.containsKey(BleUtils.AD_TYPE.GAP_ADTYPE_16BIT_MORE) ? content.get(BleUtils.AD_TYPE.GAP_ADTYPE_16BIT_MORE) : content.get(BleUtils.AD_TYPE.GAP_ADTYPE_16BIT_COMPLETE); - for(int i=0; i < uuids.length; i += 2){ - String hexUUid = String.format("%02X%02X",uuids[i+1],uuids[i]); - if( session.getConnectionUuids().contains(hexUUid) ){ + for (int i = 0; i < uuids.length; i += 2) { + String hexUUid = String.format("%02X%02X", uuids[i + 1], uuids[i]); + if (session.getConnectionUuids().contains(hexUUid)) { return true; } } @@ -176,70 +147,63 @@ private boolean containsRequiredUuids(final BleDeviceSession2 session){ return true; } - private void free(final BleDeviceSession2 session, ConnectionHandlerAction action){ - switch (action){ + private void free(final BDDeviceSessionImpl session, ConnectionHandlerAction action) { + switch (action) { case ENTRY: - break; case EXIT: break; - case CONNECT_DEVICE: - { + case CONNECT_DEVICE: { // "direct" connection, only aquire direct connection in free state - switch (session.getSessionState()){ + switch (session.getSessionState()) { case SESSION_OPEN_PARK: - case SESSION_CLOSED:{ - if( session.isConnectableAdvertisement() && containsRequiredUuids(session) ) { + case SESSION_CLOSED: { + if (session.isConnectableAdvertisement() && containsRequiredUuids(session)) { changeState(session, ConnectionHandlerState.CONNECTING); - }else{ + } else { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); } break; } - case SESSION_CLOSING:{ + case SESSION_CLOSING: { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); break; } - case SESSION_OPEN:{ + case SESSION_OPEN: { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN); break; } } break; } - case ADVERTISEMENT_HEAD_RECEIVED: - { + case ADVERTISEMENT_HEAD_RECEIVED: { // fallback - if(session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK){ - if( session.isConnectableAdvertisement() && containsRequiredUuids(session) ) { + if (session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK) { + if (session.isConnectableAdvertisement() && containsRequiredUuids(session)) { changeState(session, ConnectionHandlerState.CONNECTING); - }else{ - BleLogger.d(TAG,"Skipped connection attempt due to reason device is not in connectable advertisement or missing service"); + } else { + BleLogger.d(TAG, "Skipped connection attempt due to reason device is not in connectable advertisement or missing service"); } } break; } - case DISCONNECT_DEVICE: - { + case DISCONNECT_DEVICE: { handleDisconnectDevice(session); break; } - case DEVICE_DISCONNECTED: - { + case DEVICE_DISCONNECTED: { handleDeviceDisconnected(session); break; } - case DEVICE_CONNECTED: - { + case DEVICE_CONNECTED: { BleLogger.e(TAG, " Incorrect event received! "); break; } } } - private void connecting(final BleDeviceSession2 session, ConnectionHandlerAction action){ - switch (action){ - case ENTRY: - { + private void connecting(final BDDeviceSessionImpl session, ConnectionHandlerAction action) { + switch (action) { + case ENTRY: { scannerInterface.connectionHandlerRequestStopScanning(); current = session; updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_OPENING); @@ -250,48 +214,38 @@ private void connecting(final BleDeviceSession2 session, ConnectionHandlerAction scannerInterface.connectionHandlerResumeScanning(); break; } - case CONNECT_DEVICE: - { - if( session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSED) { + case CONNECT_DEVICE: { + if (session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSED) { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); } break; } - case ADVERTISEMENT_HEAD_RECEIVED: - { + case ADVERTISEMENT_HEAD_RECEIVED: { break; } - case DISCONNECT_DEVICE: - { - if( !session.equals(current) ) { + case DISCONNECT_DEVICE: { + if (!session.equals(current)) { handleDisconnectDevice(session); - }else{ + } else { // cancel pending connection connectionInterface.cancelDeviceConnection(session); - observers.accessAll(new AtomicSet.ObjectAccess() { - @Override - public void access(ConnectionHandlerObserver object) { - object.deviceConnectionCancelled(session); - } - }); + observer.deviceConnectionCancelled(session); updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_CLOSED); changeState(session, ConnectionHandlerState.FREE); } break; } - case DEVICE_CONNECTED: - { + case DEVICE_CONNECTED: { BleUtils.validate(current == session, "incorrect session object"); updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN); changeState(session, ConnectionHandlerState.FREE); break; } - case DEVICE_DISCONNECTED: - { - if( current == session ){ + case DEVICE_DISCONNECTED: { + if (current == session) { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); changeState(session, ConnectionHandlerState.FREE); - }else{ + } else { handleDeviceDisconnected(session); } break; @@ -299,13 +253,13 @@ public void access(ConnectionHandlerObserver object) { } } - private void handleDisconnectDevice(BleDeviceSession2 session) { - switch (session.getSessionState()){ - case SESSION_OPEN_PARK:{ + private void handleDisconnectDevice(BDDeviceSessionImpl session) { + switch (session.getSessionState()) { + case SESSION_OPEN_PARK: { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_CLOSED); break; } - case SESSION_OPEN:{ + case SESSION_OPEN: { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_CLOSING); connectionInterface.disconnectDevice(session); break; @@ -313,17 +267,17 @@ private void handleDisconnectDevice(BleDeviceSession2 session) { } } - private void handleDeviceDisconnected(BleDeviceSession2 session) { - switch (session.getSessionState()){ - case SESSION_OPEN:{ - if(automaticReconnection) { + private void handleDeviceDisconnected(BDDeviceSessionImpl session) { + switch (session.getSessionState()) { + case SESSION_OPEN: { + if (automaticReconnection) { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - }else{ + } else { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_CLOSED); } break; } - case SESSION_CLOSING:{ + case SESSION_CLOSING: { updateSessionState(session, BleDeviceSession.DeviceSessionState.SESSION_CLOSED); break; } diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionHandlerObserver.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionHandlerObserver.java new file mode 100755 index 00000000..eec1d641 --- /dev/null +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionHandlerObserver.java @@ -0,0 +1,10 @@ +package com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection; + +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.BDDeviceSessionImpl; + +public interface ConnectionHandlerObserver { + void deviceSessionStateChanged(BDDeviceSessionImpl session); + void deviceConnected(BDDeviceSessionImpl session); // explicit connected event + void deviceDisconnected(BDDeviceSessionImpl session); // explicit disconnected event + void deviceConnectionCancelled(BDDeviceSessionImpl session); // explicit pending connection cancelled event +} diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionInterface.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionInterface.java new file mode 100755 index 00000000..cdf18435 --- /dev/null +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ConnectionInterface.java @@ -0,0 +1,9 @@ +package com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection; + +import com.androidcommunications.polar.enpoints.ble.bluedroid.host.BDDeviceSessionImpl; + +public interface ConnectionInterface { + void connectDevice(BDDeviceSessionImpl session); + void disconnectDevice(BDDeviceSessionImpl session); + void cancelDeviceConnection(BDDeviceSessionImpl session); +} diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ScannerInterface.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ScannerInterface.java similarity index 81% rename from sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ScannerInterface.java rename to sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ScannerInterface.java index ea66dc42..0090ea6b 100755 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ScannerInterface.java +++ b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/bluedroid/host/connection/ScannerInterface.java @@ -1,4 +1,4 @@ -package com.androidcommunications.polar.enpoints.ble.common.connection; +package com.androidcommunications.polar.enpoints.ble.bluedroid.host.connection; public interface ScannerInterface { /** diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/BleDeviceListener2.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/BleDeviceListener2.java deleted file mode 100644 index 557e0b9c..00000000 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/BleDeviceListener2.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.androidcommunications.polar.enpoints.ble.common; - -import android.content.Context; -import android.support.annotation.IntRange; -import android.support.annotation.Nullable; -import android.util.Pair; - -import com.androidcommunications.polar.api.ble.BleDeviceListener; -import com.androidcommunications.polar.api.ble.model.BleDeviceSession; -import com.androidcommunications.polar.api.ble.model.gatt.BleGattBase; -import com.androidcommunications.polar.enpoints.ble.common.connection.ConnectionHandler; -import com.androidcommunications.polar.enpoints.ble.common.connection.ConnectionHandlerObserver; -import com.androidcommunications.polar.enpoints.ble.common.connection.ConnectionInterface; -import com.androidcommunications.polar.enpoints.ble.common.connection.ScannerInterface; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import io.reactivex.Observable; -import io.reactivex.ObservableEmitter; -import io.reactivex.ObservableOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.annotations.NonNull; -import io.reactivex.functions.Action; - -public abstract class BleDeviceListener2 extends BleDeviceListener implements ScannerInterface, ConnectionInterface { - - protected ConnectionHandler connectionHandler; - protected Context context; - private BleDeviceSessionStateChangedCallback changedCallback = null; - protected BlePowerStateChangedCallback powerStateChangedCallback = null; - - public BleDeviceListener2(final Context context, Set > clients) { - super(clients); - this.context = context; - this.connectionHandler = new ConnectionHandler(context,this,this); - this.connectionHandler.addObserver(new ConnectionHandlerObserver() { - @Override - public void deviceSessionStateChanged(final BleDeviceSession2 session) { - if (changedCallback != null) { - if (session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK && - session.getPreviousState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN) { - //NOTE special case, we were connected so propagate closed event( informal ) - changedCallback.stateChanged(session, BleDeviceSession.DeviceSessionState.SESSION_CLOSED); - if (session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK) { - changedCallback.stateChanged(session, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK); - } - } else { - changedCallback.stateChanged(session, session.getSessionState()); - } - } - } - - @Override - public void deviceConnected(BleDeviceSession2 session) { - session.discoverServices(); - } - - @Override - public void deviceDisconnected(BleDeviceSession2 session) { - session.handleDisconnection(); - session.reset(); - } - - @Override - public void deviceConnectionCancelled(BleDeviceSession2 session) { - session.reset(); - } - }); - } - - @Override - public void shutDown(){ - - } - - @Override - public void setBlePowerStateCallback(@Nullable BlePowerStateChangedCallback cb) { - this.powerStateChangedCallback = cb; - if (cb != null) { - cb.stateChanged(this.bleActive()); - } - } - - @Override - public void setDeviceSessionStateChangedCallback(@Nullable BleDeviceSessionStateChangedCallback changedCallback) { - this.changedCallback = changedCallback; - } - - @Override - public void openSessionDirect(BleDeviceSession session){ - session.setConnectionUuids(new ArrayList()); - connectionHandler.connectDevice((BleDeviceSession2)session,bleActive()); - } - - @Override - public void openSessionDirect(BleDeviceSession session, List uuids){ - session.setConnectionUuids(uuids); - connectionHandler.connectDevice((BleDeviceSession2)session,bleActive()); - } - - @Override - public Observable > monitorDeviceSessionState(final BleDeviceSession session) { - final ConnectionHandlerObserver[] observer = new ConnectionHandlerObserver[1]; - return Observable.create(new ObservableOnSubscribe>() { - @Override - public void subscribe(final @NonNull ObservableEmitter> subscriber) { - observer[0] = new ConnectionHandlerObserver() { - @Override - public void deviceSessionStateChanged(BleDeviceSession2 session1) { - if (session == null || session == session1) { - if( session1.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK && - session1.getPreviousState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN ){ - //NOTE special case, we were connected so propagate closed event( informal ) - subscriber.onNext(new Pair<>((BleDeviceSession) session1, BleDeviceSession.DeviceSessionState.SESSION_CLOSED)); - if(session1.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK) { - subscriber.onNext(new Pair<>((BleDeviceSession)session1, BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK)); - } - } else { - subscriber.onNext(new Pair<>((BleDeviceSession)session1, session1.getSessionState())); - } - } - } - - @Override - public void deviceConnected(BleDeviceSession2 session) { - // do nothing - } - - @Override - public void deviceDisconnected(BleDeviceSession2 session) { - // do nothing - } - - @Override - public void deviceConnectionCancelled(BleDeviceSession2 session) { - - } - }; - connectionHandler.addObserver(observer[0]); - } - }).doFinally(new Action() { - @Override - public void run() { - connectionHandler.removeObserver(observer[0]); - } - }).subscribeOn(AndroidSchedulers.from(context.getMainLooper())); - } - - @Override - public void closeSessionDirect(BleDeviceSession session){ - connectionHandler.disconnectDevice((BleDeviceSession2)session); - } - - @Override - public void setAutomaticReconnection(boolean automaticReconnection){ - connectionHandler.setAutomaticReconnection(automaticReconnection); - } -} diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/BleDeviceSession2.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/BleDeviceSession2.java deleted file mode 100644 index 89e9298f..00000000 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/BleDeviceSession2.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.androidcommunications.polar.enpoints.ble.common; - -import com.androidcommunications.polar.api.ble.BleLogger; -import com.androidcommunications.polar.api.ble.model.BleDeviceSession; -import com.androidcommunications.polar.enpoints.ble.common.attribute.AttributeOperation; - -import io.reactivex.functions.Action; - -public abstract class BleDeviceSession2 extends BleDeviceSession { - public static final String TAG = BleDeviceSession2.class.getSimpleName(); - - public BleDeviceSession2() { - super(); - } - - protected void logIfError(final String message, int status){ - if(status!=0) { - BleLogger.e(TAG, message + " Failed with error: " + status); - } - } - - /** - * Internal use only - * @param sessionState @see BleDeviceSession.DeviceSessionState - */ - public void setSessionState(BleDeviceSession.DeviceSessionState sessionState) { - this.previousState = this.state; - this.state = sessionState; - } - - public abstract boolean sendNextAttributeOperation(AttributeOperation attributeOperation) throws Throwable; - public abstract void discoverServices(); - public abstract void handleDisconnection(); - public abstract void startAuthentication(Action complete); - public abstract void reset(); -} diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionHandlerObserver.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionHandlerObserver.java deleted file mode 100755 index ae0f25c2..00000000 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionHandlerObserver.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.androidcommunications.polar.enpoints.ble.common.connection; - -import com.androidcommunications.polar.enpoints.ble.common.BleDeviceSession2; - -public interface ConnectionHandlerObserver { - void deviceSessionStateChanged(BleDeviceSession2 session); - void deviceConnected(BleDeviceSession2 session); // explicit connected event - void deviceDisconnected(BleDeviceSession2 session); // explicit disconnected event - void deviceConnectionCancelled(BleDeviceSession2 session); // explicit pending connection cancelled event -} diff --git a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionInterface.java b/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionInterface.java deleted file mode 100755 index 3e9a0068..00000000 --- a/sources/Android/android-communications/src/main/java/com/androidcommunications/polar/enpoints/ble/common/connection/ConnectionInterface.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.androidcommunications.polar.enpoints.ble.common.connection; - -import com.androidcommunications.polar.enpoints.ble.common.BleDeviceSession2; - -public interface ConnectionInterface { - void connectDevice(BleDeviceSession2 session); - void disconnectDevice(BleDeviceSession2 session); - void cancelDeviceConnection(BleDeviceSession2 session); -} diff --git a/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApi.java b/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApi.java index 59565269..36c2e592 100644 --- a/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApi.java +++ b/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApi.java @@ -10,10 +10,10 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import io.reactivex.Completable; -import io.reactivex.Flowable; -import io.reactivex.Single; -import io.reactivex.annotations.NonNull; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.annotations.NonNull; import polar.com.sdk.api.errors.PolarInvalidArgument; import polar.com.sdk.api.model.PolarAccelerometerData; import polar.com.sdk.api.model.PolarBiozData; diff --git a/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApiCallback.java b/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApiCallback.java index 81688cb7..f77247f9 100644 --- a/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApiCallback.java +++ b/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApiCallback.java @@ -1,9 +1,10 @@ // Copyright © 2019 Polar Electro Oy. All rights reserved. package polar.com.sdk.api; +import android.support.annotation.NonNull; + import java.util.UUID; -import io.reactivex.annotations.NonNull; import polar.com.sdk.api.model.PolarDeviceInfo; import polar.com.sdk.api.model.PolarHrData; diff --git a/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApiCallbackProvider.java b/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApiCallbackProvider.java index f1400385..047caae3 100644 --- a/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApiCallbackProvider.java +++ b/sources/Android/android-communications/src/main/java/polar/com/sdk/api/PolarBleApiCallbackProvider.java @@ -1,8 +1,9 @@ // Copyright © 2019 Polar Electro Oy. All rights reserved. package polar.com.sdk.api; +import android.support.annotation.NonNull; + import java.util.UUID; -import io.reactivex.annotations.NonNull; import polar.com.sdk.api.model.PolarDeviceInfo; import polar.com.sdk.api.model.PolarHrData; diff --git a/sources/Android/android-communications/src/main/java/polar/com/sdk/impl/BDBleApiImpl.java b/sources/Android/android-communications/src/main/java/polar/com/sdk/impl/BDBleApiImpl.java index 6fa0daff..06831a96 100644 --- a/sources/Android/android-communications/src/main/java/polar/com/sdk/impl/BDBleApiImpl.java +++ b/sources/Android/android-communications/src/main/java/polar/com/sdk/impl/BDBleApiImpl.java @@ -13,7 +13,6 @@ import com.androidcommunications.polar.api.ble.BleLogger; import com.androidcommunications.polar.api.ble.exceptions.BleDisconnected; import com.androidcommunications.polar.api.ble.model.BleDeviceSession; -import com.androidcommunications.polar.api.ble.model.advertisement.BleAdvertisementContent; import com.androidcommunications.polar.api.ble.model.advertisement.BlePolarHrAdvertisement; import com.androidcommunications.polar.api.ble.model.gatt.BleGattBase; import com.androidcommunications.polar.api.ble.model.gatt.client.BleBattClient; @@ -32,7 +31,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; -import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -46,22 +44,12 @@ import fi.polar.remote.representation.protobuf.ExerciseSamples; import fi.polar.remote.representation.protobuf.Types; -import io.reactivex.Completable; -import io.reactivex.CompletableEmitter; -import io.reactivex.CompletableOnSubscribe; -import io.reactivex.CompletableSource; -import io.reactivex.Flowable; -import io.reactivex.Scheduler; -import io.reactivex.Single; -import io.reactivex.SingleSource; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.functions.Action; -import io.reactivex.functions.BiFunction; -import io.reactivex.functions.Consumer; -import io.reactivex.functions.Function; -import io.reactivex.functions.Predicate; -import io.reactivex.schedulers.Timed; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.functions.BiFunction; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.schedulers.Timed; import polar.com.sdk.api.PolarBleApi; import polar.com.sdk.api.PolarBleApiCallbackProvider; import polar.com.sdk.api.errors.PolarDeviceDisconnected; @@ -101,12 +89,7 @@ public class BDBleApiImpl extends PolarBleApi implements protected PolarBleApiCallbackProvider callback; protected PolarBleApiLogger logger; protected static final int ANDROID_VERSION_O = 26; - BleDeviceListener.BleSearchPreFilter filter = new BleDeviceListener.BleSearchPreFilter() { - @Override - public boolean process(BleAdvertisementContent content) { - return content.getPolarDeviceId().length() != 0 && !content.getPolarDeviceType().equals("mobile"); - } - }; + protected BleDeviceListener.BleSearchPreFilter filter = content -> content.getPolarDeviceId().length() != 0 && !content.getPolarDeviceType().equals("mobile"); @SuppressLint({"NewApi", "CheckResult"}) public BDBleApiImpl(final Context context, int features) { @@ -265,12 +248,7 @@ protected Single querySettings(final String identifier, fina try { final BleDeviceSession session = sessionPmdClientReady(identifier); final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE); - return client.querySettings(type).map(new Function() { - @Override - public PolarSensorSetting apply(BlePMDClient.PmdSetting setting) throws Exception { - return new PolarSensorSetting(setting.settings, type); - } - }); + return client.querySettings(type).map(setting -> new PolarSensorSetting(setting.settings, type)); } catch (Throwable e) { return Single.error(e); } @@ -289,54 +267,34 @@ public void foregroundEntered() { @Override public Completable autoConnectToDevice(final int rssiLimit, final String service, final int timeout, final TimeUnit unit, final String polarDeviceType) { final long[] start = {0}; - return Completable.create(new CompletableOnSubscribe() { - @Override - public void subscribe(CompletableEmitter emitter) throws Exception { - if (service == null || service.matches("([0-9a-fA-F]{4})")) { - emitter.onComplete(); - } else { - emitter.tryOnError(new PolarInvalidArgument("Invalid service string format")); - } + return Completable.create(emitter -> { + if (service == null || service.matches("([0-9a-fA-F]{4})")) { + emitter.onComplete(); + } else { + emitter.tryOnError(new PolarInvalidArgument("Invalid service string format")); } - }).andThen(listener.search(false).filter(new Predicate() { - @Override - public boolean test(BleDeviceSession bleDeviceSession) throws Exception { - if (bleDeviceSession.getMedianRssi() >= rssiLimit && - bleDeviceSession.isConnectableAdvertisement() && - (polarDeviceType == null || polarDeviceType.equals(bleDeviceSession.getPolarDeviceType())) && - (service == null || bleDeviceSession.getAdvertisementContent().containsService(service))) { - if (start[0] == 0) { - start[0] = System.currentTimeMillis(); - } - return true; + }).andThen(listener.search(false).filter(bleDeviceSession -> { + if (bleDeviceSession.getMedianRssi() >= rssiLimit && + bleDeviceSession.isConnectableAdvertisement() && + (polarDeviceType == null || polarDeviceType.equals(bleDeviceSession.getPolarDeviceType())) && + (service == null || bleDeviceSession.getAdvertisementContent().containsService(service))) { + if (start[0] == 0) { + start[0] = System.currentTimeMillis(); } - return false; - } - }).timestamp().takeUntil(new Predicate>() { - @Override - public boolean test(Timed bleDeviceSessionTimed) throws Exception { - long diff = bleDeviceSessionTimed.time(TimeUnit.MILLISECONDS) - start[0]; - return (diff >= unit.toMillis(timeout)); - } - }).reduce(new HashSet(), new BiFunction, Timed, Set>() { - @Override - public Set apply(Set objects, Timed bleDeviceSessionTimed) throws Exception { - objects.add(bleDeviceSessionTimed.value()); - return objects; - } - }).doOnSuccess(new Consumer>() { - @Override - public void accept(Set set) throws Exception { - List list = new ArrayList<>(set); - Collections.sort(list, new Comparator() { - @Override - public int compare(BleDeviceSession o1, BleDeviceSession o2) { - return o1.getRssi() > o2.getRssi() ? -1 : 1; - } - }); - listener.openSessionDirect(list.get(0)); - log("auto connect search complete"); + return true; } + return false; + }).timestamp().takeUntil(bleDeviceSessionTimed -> { + long diff = bleDeviceSessionTimed.time(TimeUnit.MILLISECONDS) - start[0]; + return (diff >= unit.toMillis(timeout)); + }).reduce(new HashSet<>(), (BiFunction, Timed, Set>) (objects, bleDeviceSessionTimed) -> { + objects.add(bleDeviceSessionTimed.value()); + return objects; + }).doOnSuccess(set -> { + List list = new ArrayList<>(set); + Collections.sort(list, (o1, o2) -> o1.getRssi() > o2.getRssi() ? -1 : 1); + listener.openSessionDirect(list.get(0)); + log("auto connect search complete"); }).toObservable().ignoreElements()); } @@ -356,32 +314,12 @@ public void connectToDevice(final String identifier) throws PolarInvalidArgument if (session != null) { listener.openSessionDirect(session); } else { - connectSubscriptions.put(identifier, listener.search(false).filter(new Predicate() { - @Override - public boolean test(BleDeviceSession bleDeviceSession) throws Exception { - return identifier.contains(":") ? - bleDeviceSession.getAddress().equals(identifier) : - bleDeviceSession.getPolarDeviceId().equals(identifier); - } - }).take(1).observeOn(scheduler).subscribe( - new Consumer() { - @Override - public void accept(BleDeviceSession bleDeviceSession) throws Exception { - listener.openSessionDirect(bleDeviceSession); - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - logError(throwable.getMessage()); - } - }, - new Action() { - @Override - public void run() throws Exception { - log("connect search complete"); - } - } + connectSubscriptions.put(identifier, listener.search(false).filter(bleDeviceSession -> identifier.contains(":") ? + bleDeviceSession.getAddress().equals(identifier) : + bleDeviceSession.getPolarDeviceId().equals(identifier)).take(1).observeOn(scheduler).subscribe( + bleDeviceSession -> listener.openSessionDirect(bleDeviceSession), + throwable -> logError(throwable.getMessage()), + () -> log("connect search complete") )); } } @@ -415,12 +353,7 @@ public Completable startRecording(String identifier, String exerciseId, Recordin Types.PbDuration duration = Types.PbDuration.newBuilder().setSeconds(interval.getValue()).build(); PftpRequest.PbPFtpRequestStartRecordingParams params = PftpRequest.PbPFtpRequestStartRecordingParams.newBuilder(). setSampleDataIdentifier(exerciseId).setSampleType(t).setRecordingInterval(duration).build(); - return client.query(PftpRequest.PbPFtpQuery.REQUEST_START_RECORDING_VALUE, params.toByteArray()).toObservable().ignoreElements().onErrorResumeNext(new Function() { - @Override - public CompletableSource apply(Throwable throwable) throws Exception { - return Completable.error(throwable); - } - }); + return client.query(PftpRequest.PbPFtpQuery.REQUEST_START_RECORDING_VALUE, params.toByteArray()).toObservable().ignoreElements().onErrorResumeNext(Completable::error); } return Completable.error(new PolarOperationNotSupported()); } catch (Throwable error) { @@ -434,12 +367,7 @@ public Completable stopRecording(String identifier) { final BleDeviceSession session = sessionPsFtpClientReady(identifier); final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE); if (session.getPolarDeviceType().equals("H10")) { - return client.query(PftpRequest.PbPFtpQuery.REQUEST_STOP_RECORDING_VALUE, null).toObservable().ignoreElements().onErrorResumeNext(new Function() { - @Override - public CompletableSource apply(Throwable throwable) throws Exception { - return Completable.error(handleError(throwable)); - } - }); + return client.query(PftpRequest.PbPFtpQuery.REQUEST_STOP_RECORDING_VALUE, null).toObservable().ignoreElements().onErrorResumeNext(throwable -> Completable.error(handleError(throwable))); } return Completable.error(new PolarOperationNotSupported()); } catch (Throwable error) { @@ -453,18 +381,10 @@ public Single> requestRecordingStatus(String identifier) { final BleDeviceSession session = sessionPsFtpClientReady(identifier); final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE); if (session.getPolarDeviceType().equals("H10")) { - return client.query(PftpRequest.PbPFtpQuery.REQUEST_RECORDING_STATUS_VALUE, null).map(new Function>() { - @Override - public Pair apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception { - PftpResponse.PbRequestRecordingStatusResult result = PftpResponse.PbRequestRecordingStatusResult.parseFrom(byteArrayOutputStream.toByteArray()); - return new Pair<>(result.getRecordingOn(), result.hasSampleDataIdentifier() ? result.getSampleDataIdentifier() : ""); - } - }).onErrorResumeNext(new Function>>() { - @Override - public SingleSource> apply(Throwable throwable) throws Exception { - return Single.error(handleError(throwable)); - } - }); + return client.query(PftpRequest.PbPFtpQuery.REQUEST_RECORDING_STATUS_VALUE, null).map(byteArrayOutputStream -> { + PftpResponse.PbRequestRecordingStatusResult result = PftpResponse.PbRequestRecordingStatusResult.parseFrom(byteArrayOutputStream.toByteArray()); + return new Pair<>(result.getRecordingOn(), result.hasSampleDataIdentifier() ? result.getSampleDataIdentifier() : ""); + }).onErrorResumeNext(throwable -> Single.error(handleError(throwable))); } return Single.error(new PolarOperationNotSupported()); } catch (Throwable error) { @@ -479,47 +399,21 @@ public Flowable listExercises(String identifier) { final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE); switch (session.getPolarDeviceType()) { case "OH1": - return fetchRecursively(client, "/U/0/", new FetchRecursiveCondition() { - @Override - public boolean include(String entry) { - return entry.matches("^([0-9]{8})(\\/)") || - entry.matches("^([0-9]{6})(\\/)") || - entry.equals("E/") || - entry.equals("SAMPLES.BPB") || - entry.equals("00/"); - } - }).map(new Function() { - @Override - public PolarExerciseEntry apply(String p) throws Exception { - String[] components = p.split("/"); - SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd HHmmss", Locale.getDefault()); - Date date = format.parse(components[3] + " " + components[5]); - return new PolarExerciseEntry(p, date, components[3] + components[5]); - } - }).onErrorResumeNext(new Function>() { - @Override - public Publisher apply(Throwable throwable) throws Exception { - return Flowable.error(handleError(throwable)); - } - }); + return fetchRecursively(client, "/U/0/", entry -> entry.matches("^([0-9]{8})(\\/)") || + entry.matches("^([0-9]{6})(\\/)") || + entry.equals("E/") || + entry.equals("SAMPLES.BPB") || + entry.equals("00/")).map(p -> { + String[] components = p.split("/"); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd HHmmss", Locale.getDefault()); + Date date = format.parse(components[3] + " " + components[5]); + return new PolarExerciseEntry(p, date, components[3] + components[5]); + }).onErrorResumeNext(throwable -> Flowable.error(handleError(throwable))); case "H10": - return fetchRecursively(client, "/", new FetchRecursiveCondition() { - @Override - public boolean include(String entry) { - return entry.endsWith("/") || entry.equals("SAMPLES.BPB"); - } - }).map(new Function() { - @Override - public PolarExerciseEntry apply(String p) throws Exception { - String[] components = p.split("/"); - return new PolarExerciseEntry(p, new Date(), components[1]); - } - }).onErrorResumeNext(new Function>() { - @Override - public Publisher apply(Throwable throwable) throws Exception { - return Flowable.error(handleError(throwable)); - } - }); + return fetchRecursively(client, "/", entry -> entry.endsWith("/") || entry.equals("SAMPLES.BPB")).map(p -> { + String[] components = p.split("/"); + return new PolarExerciseEntry(p, new Date(), components[1]); + }).onErrorResumeNext(throwable -> Flowable.error(handleError(throwable))); default: return Flowable.error(new PolarOperationNotSupported()); } @@ -553,22 +447,14 @@ TODO to improve throughput (device will request parameter update from mobile): ); */ - return client.request(builder.build().toByteArray()).map(new Function() { - @Override - public PolarExerciseData apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception { - ExerciseSamples.PbExerciseSamples samples = ExerciseSamples.PbExerciseSamples.parseFrom(byteArrayOutputStream.toByteArray()); - if (samples.hasRrSamples()) { - return new PolarExerciseData(samples.getRecordingInterval().getSeconds(), samples.getRrSamples().getRrIntervalsList()); - } else { - return new PolarExerciseData(samples.getRecordingInterval().getSeconds(), samples.getHeartRateSamplesList()); - } - } - }).onErrorResumeNext(new Function>() { - @Override - public SingleSource apply(Throwable throwable) throws Exception { - return Single.error(handleError(throwable)); + return client.request(builder.build().toByteArray()).map(byteArrayOutputStream -> { + ExerciseSamples.PbExerciseSamples samples = ExerciseSamples.PbExerciseSamples.parseFrom(byteArrayOutputStream.toByteArray()); + if (samples.hasRrSamples()) { + return new PolarExerciseData(samples.getRecordingInterval().getSeconds(), samples.getRrSamples().getRrIntervalsList()); + } else { + return new PolarExerciseData(samples.getRecordingInterval().getSeconds(), samples.getHeartRateSamplesList()); } - }); + }).onErrorResumeNext(throwable -> Single.error(handleError(throwable))); } return Single.error(new PolarOperationNotSupported()); } catch (Throwable error) { @@ -587,37 +473,24 @@ public Completable removeExercise(String identifier, PolarExerciseEntry entry) { final String[] components = entry.path.split("/"); final String exerciseParent = "/U/0/" + components[3] + "/E/"; builder.setPath(exerciseParent); - return client.request(builder.build().toByteArray()).flatMap(new Function>() { - @Override - public SingleSource apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception { - PftpResponse.PbPFtpDirectory directory = PftpResponse.PbPFtpDirectory.parseFrom(byteArrayOutputStream.toByteArray()); - protocol.PftpRequest.PbPFtpOperation.Builder removeBuilder = protocol.PftpRequest.PbPFtpOperation.newBuilder(); - removeBuilder.setCommand(PftpRequest.PbPFtpOperation.Command.REMOVE); - if (directory.getEntriesCount() <= 1) { - // remove entire directory - removeBuilder.setPath("/U/0/" + components[3] + "/"); - } else { - // remove only exercise - removeBuilder.setPath("/U/0/" + components[3] + "/E/" + components[5] + "/"); - } - return client.request(removeBuilder.build().toByteArray()); - } - }).toObservable().ignoreElements().onErrorResumeNext(new Function() { - @Override - public CompletableSource apply(Throwable throwable) throws Exception { - return Completable.error(handleError(throwable)); + return client.request(builder.build().toByteArray()).flatMap((Function>) byteArrayOutputStream -> { + PftpResponse.PbPFtpDirectory directory = PftpResponse.PbPFtpDirectory.parseFrom(byteArrayOutputStream.toByteArray()); + PftpRequest.PbPFtpOperation.Builder removeBuilder = PftpRequest.PbPFtpOperation.newBuilder(); + removeBuilder.setCommand(PftpRequest.PbPFtpOperation.Command.REMOVE); + if (directory.getEntriesCount() <= 1) { + // remove entire directory + removeBuilder.setPath("/U/0/" + components[3] + "/"); + } else { + // remove only exercise + removeBuilder.setPath("/U/0/" + components[3] + "/E/" + components[5] + "/"); } - }); + return client.request(removeBuilder.build().toByteArray()); + }).toObservable().ignoreElements().onErrorResumeNext(throwable -> Completable.error(handleError(throwable))); } else if (session.getPolarDeviceType().equals("H10")) { protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder(); builder.setCommand(PftpRequest.PbPFtpOperation.Command.REMOVE); builder.setPath(entry.path); - return client.request(builder.build().toByteArray()).toObservable().ignoreElements().onErrorResumeNext(new Function() { - @Override - public CompletableSource apply(Throwable throwable) throws Exception { - return Completable.error(handleError(throwable)); - } - }); + return client.request(builder.build().toByteArray()).toObservable().ignoreElements().onErrorResumeNext(throwable -> Completable.error(handleError(throwable))); } return Completable.error(new PolarOperationNotSupported()); } catch (Throwable error) { @@ -627,39 +500,26 @@ public CompletableSource apply(Throwable throwable) throws Exception { @Override public Flowable searchForDevice() { - return listener.search(false).distinct().map(new Function() { - @Override - public PolarDeviceInfo apply(BleDeviceSession bleDeviceSession) throws Exception { - return new PolarDeviceInfo(bleDeviceSession.getPolarDeviceId(), - bleDeviceSession.getAddress(), - bleDeviceSession.getRssi(), - bleDeviceSession.getName(), - bleDeviceSession.isConnectableAdvertisement()); - } - }); + return listener.search(false).distinct().map(bleDeviceSession -> new PolarDeviceInfo(bleDeviceSession.getPolarDeviceId(), + bleDeviceSession.getAddress(), + bleDeviceSession.getRssi(), + bleDeviceSession.getName(), + bleDeviceSession.isConnectableAdvertisement())); } @Override public Flowable startListenForPolarHrBroadcasts(final Set deviceIds) { // set filter to null, NOTE this disables reconnection in background - return listener.search(false).filter(new Predicate() { - @Override - public boolean test(BleDeviceSession bleDeviceSession) throws Exception { - return (deviceIds == null || deviceIds.contains(bleDeviceSession.getPolarDeviceId())) && - bleDeviceSession.getAdvertisementContent().getPolarHrAdvertisement().isPresent(); - } - }).map(new Function() { - @Override - public PolarHrBroadcastData apply(BleDeviceSession bleDeviceSession) throws Exception { - BlePolarHrAdvertisement advertisement = bleDeviceSession.getBlePolarHrAdvertisement(); - return new PolarHrBroadcastData(new PolarDeviceInfo(bleDeviceSession.getPolarDeviceId(), - bleDeviceSession.getAddress(), - bleDeviceSession.getRssi(), - bleDeviceSession.getName(), - bleDeviceSession.isConnectableAdvertisement()), - advertisement.getHrForDisplay(), - advertisement.getBatteryStatus() != 0); - } + return listener.search(false).filter(bleDeviceSession -> (deviceIds == null || deviceIds.contains(bleDeviceSession.getPolarDeviceId())) && + bleDeviceSession.getAdvertisementContent().getPolarHrAdvertisement().isPresent()).map(bleDeviceSession -> { + BlePolarHrAdvertisement advertisement = bleDeviceSession.getBlePolarHrAdvertisement(); + return new PolarHrBroadcastData(new PolarDeviceInfo(bleDeviceSession.getPolarDeviceId(), + bleDeviceSession.getAddress(), + bleDeviceSession.getRssi(), + bleDeviceSession.getName(), + bleDeviceSession.isConnectableAdvertisement()), + advertisement.getHrForDisplay(), + advertisement.getBatteryStatus() != 0); }); } @@ -670,26 +530,13 @@ public Flowable startEcgStreaming(String identifier, final BleDeviceSession session = sessionPmdClientReady(identifier); final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE); return client.startMeasurement(BlePMDClient.PmdMeasurementType.ECG, setting.map2PmdSettings()).andThen( - client.monitorEcgNotifications(true).map(new Function() { - @Override - public PolarEcgData apply(BlePMDClient.EcgData ecgData) throws Exception { - List samples = new ArrayList<>(); - for (BlePMDClient.EcgData.EcgSample s : ecgData.ecgSamples) { - samples.add(s.microVolts); - } - return new PolarEcgData(samples, ecgData.timeStamp); - } - }).onErrorResumeNext(new Function>() { - @Override - public Publisher apply(Throwable throwable) throws Exception { - return Flowable.error(handleError(throwable)); - } - }).doFinally(new Action() { - @Override - public void run() throws Exception { - stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.ECG); + client.monitorEcgNotifications(true).map(ecgData -> { + List samples = new ArrayList<>(); + for (BlePMDClient.EcgData.EcgSample s : ecgData.ecgSamples) { + samples.add(s.microVolts); } - })); + return new PolarEcgData(samples, ecgData.timeStamp); + }).onErrorResumeNext(throwable -> Flowable.error(handleError(throwable))).doFinally(() -> stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.ECG))); } catch (Throwable t) { return Flowable.error(t); } @@ -702,26 +549,13 @@ public Flowable startAccStreaming(String identifier, final BleDeviceSession session = sessionPmdClientReady(identifier); final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE); return client.startMeasurement(BlePMDClient.PmdMeasurementType.ACC, setting.map2PmdSettings()).andThen( - client.monitorAccNotifications(true).map(new Function() { - @Override - public PolarAccelerometerData apply(BlePMDClient.AccData accData) throws Exception { - List samples = new ArrayList<>(); - for (BlePMDClient.AccData.AccSample s : accData.accSamples) { - samples.add(new PolarAccelerometerData.PolarAccelerometerSample(s.x, s.y, s.z)); - } - return new PolarAccelerometerData(samples, accData.timeStamp); - } - }).onErrorResumeNext(new Function>() { - @Override - public Publisher apply(Throwable throwable) throws Exception { - return Flowable.error(handleError(throwable)); + client.monitorAccNotifications(true).map(accData -> { + List samples = new ArrayList<>(); + for (BlePMDClient.AccData.AccSample s : accData.accSamples) { + samples.add(new PolarAccelerometerData.PolarAccelerometerSample(s.x, s.y, s.z)); } - }).doFinally(new Action() { - @Override - public void run() throws Exception { - stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.ACC); - } - })); + return new PolarAccelerometerData(samples, accData.timeStamp); + }).onErrorResumeNext(throwable -> Flowable.error(handleError(throwable))).doFinally(() -> stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.ACC))); } catch (Throwable t) { return Flowable.error(t); } @@ -734,26 +568,13 @@ public Flowable startOhrPPGStreaming(String identifier, final BleDeviceSession session = sessionPmdClientReady(identifier); final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE); return client.startMeasurement(BlePMDClient.PmdMeasurementType.PPG, setting.map2PmdSettings()).andThen( - client.monitorPpgNotifications(true).map(new Function() { - @Override - public PolarOhrPPGData apply(BlePMDClient.PpgData ppgData) throws Exception { - List samples = new ArrayList<>(); - for (BlePMDClient.PpgData.PpgSample s : ppgData.ppgSamples) { - samples.add(new PolarOhrPPGData.PolarOhrPPGSample(s.ppg0, s.ppg1, s.ppg2, s.ambient, s.ppgDataSamples, s.ambient1, s.status)); - } - return new PolarOhrPPGData(samples, ppgData.timeStamp, ppgData.type); + client.monitorPpgNotifications(true).map(ppgData -> { + List samples = new ArrayList<>(); + for (BlePMDClient.PpgData.PpgSample s : ppgData.ppgSamples) { + samples.add(new PolarOhrPPGData.PolarOhrPPGSample(s.ppg0, s.ppg1, s.ppg2, s.ambient, s.ppgDataSamples, s.ambient1, s.status)); } - }).doFinally(new Action() { - @Override - public void run() throws Exception { - stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.PPG); - } - })).onErrorResumeNext(new Function>() { - @Override - public Publisher apply(Throwable throwable) throws Exception { - return Flowable.error(handleError(throwable)); - } - }); + return new PolarOhrPPGData(samples, ppgData.timeStamp, ppgData.type); + }).doFinally(() -> stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.PPG))).onErrorResumeNext(throwable -> Flowable.error(handleError(throwable))); } catch (Throwable t) { return Flowable.error(t); } @@ -764,32 +585,19 @@ public Flowable startOhrPPIStreaming(String identifier) { try { final BleDeviceSession session = sessionPmdClientReady(identifier); final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE); - return client.startMeasurement(BlePMDClient.PmdMeasurementType.PPI, new BlePMDClient.PmdSetting(new HashMap())).andThen( - client.monitorPpiNotifications(true).map(new Function() { - @Override - public PolarOhrPPIData apply(BlePMDClient.PpiData ppiData) throws Exception { - List samples = new ArrayList<>(); - for (BlePMDClient.PpiData.PPSample ppSample : ppiData.ppSamples) { - samples.add(new PolarOhrPPIData.PolarOhrPPISample(ppSample.ppInMs, - ppSample.ppErrorEstimate, - ppSample.hr, - ppSample.blockerBit != 0, - ppSample.skinContactStatus != 0, - ppSample.skinContactSupported != 0)); - } - return new PolarOhrPPIData(ppiData.timestamp, samples); + return client.startMeasurement(BlePMDClient.PmdMeasurementType.PPI, new BlePMDClient.PmdSetting(new HashMap<>())).andThen( + client.monitorPpiNotifications(true).map(ppiData -> { + List samples = new ArrayList<>(); + for (BlePMDClient.PpiData.PPSample ppSample : ppiData.ppSamples) { + samples.add(new PolarOhrPPIData.PolarOhrPPISample(ppSample.ppInMs, + ppSample.ppErrorEstimate, + ppSample.hr, + ppSample.blockerBit != 0, + ppSample.skinContactStatus != 0, + ppSample.skinContactSupported != 0)); } - }).doFinally(new Action() { - @Override - public void run() throws Exception { - stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.PPI); - } - })).onErrorResumeNext(new Function>() { - @Override - public Publisher apply(Throwable throwable) throws Exception { - return Flowable.error(handleError(throwable)); - } - }); + return new PolarOhrPPIData(ppiData.timestamp, samples); + }).doFinally(() -> stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.PPI))).onErrorResumeNext(throwable -> Flowable.error(handleError(throwable))); } catch (Throwable t) { return Flowable.error(t); } @@ -801,22 +609,7 @@ public Flowable startBiozStreaming(final String identifier, Polar final BleDeviceSession session = sessionPmdClientReady(identifier); final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE); return client.startMeasurement(BlePMDClient.PmdMeasurementType.BIOZ, setting.map2PmdSettings()).andThen( - client.monitorBiozNotifications(true).map(new Function() { - @Override - public PolarBiozData apply(BlePMDClient.BiozData biozData) throws Exception { - return new PolarBiozData(biozData.timeStamp, biozData.samples, biozData.status, biozData.type); - } - }).doFinally(new Action() { - @Override - public void run() throws Exception { - stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.PPG); - } - })).onErrorResumeNext(new Function>() { - @Override - public Publisher apply(Throwable throwable) throws Exception { - return Flowable.error(handleError(throwable)); - } - }); + client.monitorBiozNotifications(true).map(biozData -> new PolarBiozData(biozData.timeStamp, biozData.samples, biozData.status, biozData.type)).doFinally(() -> stopPmdStreaming(session, client, BlePMDClient.PmdMeasurementType.PPG))).onErrorResumeNext(throwable -> Flowable.error(handleError(throwable))); } catch (Throwable t) { return Flowable.error(t); } @@ -831,7 +624,7 @@ protected BleDeviceSession fetchSession(final String identifier) throws PolarInv throw new PolarInvalidArgument(); } - protected BleDeviceSession sessionByAddress(final String address) throws PolarInvalidArgument { + protected BleDeviceSession sessionByAddress(final String address) { for (BleDeviceSession session : listener.deviceSessions()) { if (session.getAddress().equals(address)) { return session; @@ -840,7 +633,7 @@ protected BleDeviceSession sessionByAddress(final String address) throws PolarIn return null; } - protected BleDeviceSession sessionByDeviceId(final String deviceId) throws PolarInvalidArgument { + protected BleDeviceSession sessionByDeviceId(final String deviceId) { for (BleDeviceSession session : listener.deviceSessions()) { if (session.getAdvertisementContent().getPolarDeviceId().equals(deviceId)) { return session; @@ -892,18 +685,8 @@ protected void stopPmdStreaming(BleDeviceSession session, BlePMDClient client, B if (session.getSessionState() == SESSION_OPEN) { // stop streaming client.stopMeasurement(type).subscribe( - new Action() { - @Override - public void run() throws Exception { - - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - logError("failed to stop pmd stream: " + throwable.getLocalizedMessage()); - } - } + () -> { }, + throwable -> logError("failed to stop pmd stream: " + throwable.getLocalizedMessage()) ); } } @@ -912,64 +695,41 @@ public void accept(Throwable throwable) throws Exception { protected void setupDevice(final BleDeviceSession session) { final String deviceId = session.getPolarDeviceId().length() != 0 ? session.getPolarDeviceId() : session.getAddress(); session.monitorServicesDiscovered(true).observeOn(scheduler).toFlowable().flatMapIterable( - new Function, Iterable>() { - @Override - public Iterable apply(List uuids) throws Exception { - return uuids; + (Function, Iterable>) uuids -> uuids + ).flatMap(uuid -> { + if (session.fetchClient(uuid) != null) { + if (uuid.equals(BleHrClient.HR_SERVICE)) { + if (callback != null) { + callback.hrFeatureReady(deviceId); } - } - ).flatMap(new Function>() { - @Override - public Publisher apply(UUID uuid) throws Exception { - if (session.fetchClient(uuid) != null) { - if (uuid.equals(BleHrClient.HR_SERVICE)) { - if (callback != null) { - callback.hrFeatureReady(deviceId); - } - final BleHrClient client = (BleHrClient) session.fetchClient(BleHrClient.HR_SERVICE); - client.observeHrNotifications(true).observeOn(scheduler).subscribe( - new Consumer() { - @Override - public void accept(BleHrClient.HrNotificationData hrNotificationData) throws Exception { - if (callback != null) { - callback.hrNotificationReceived(deviceId, - new PolarHrData(hrNotificationData.hrValue, - hrNotificationData.rrs, - hrNotificationData.sensorContact, - hrNotificationData.sensorContactSupported, - hrNotificationData.rrPresent)); - } - } - }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - logError(throwable.getMessage()); - } - }, - new Action() { - @Override - public void run() throws Exception { - - } - } - ); - } else if (uuid.equals(BleBattClient.BATTERY_SERVICE)) { - BleBattClient client = (BleBattClient) session.fetchClient(BleBattClient.BATTERY_SERVICE); - return client.waitBatteryLevelUpdate(true).observeOn(scheduler).doOnSuccess(new Consumer() { - @Override - public void accept(Integer integer) throws Exception { + final BleHrClient client = (BleHrClient) session.fetchClient(BleHrClient.HR_SERVICE); + client.observeHrNotifications(true).observeOn(scheduler).subscribe( + hrNotificationData -> { if (callback != null) { - callback.batteryLevelReceived(deviceId, integer); + callback.hrNotificationReceived(deviceId, + new PolarHrData(hrNotificationData.hrValue, + hrNotificationData.rrs, + hrNotificationData.sensorContact, + hrNotificationData.sensorContactSupported, + hrNotificationData.rrPresent)); } + }, + throwable -> logError(throwable.getMessage()), + () -> { + } - }).toFlowable(); - } else if (uuid.equals(BlePMDClient.PMD_SERVICE)) { - final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE); - return client.waitNotificationEnabled(BlePMDClient.PMD_CP, true). - concatWith(client.waitNotificationEnabled(BlePMDClient.PMD_DATA, true)).andThen(client.readFeature(true).doOnSuccess(new Consumer() { - @Override - public void accept(BlePMDClient.PmdFeature pmdFeature) { + ); + } else if (uuid.equals(BleBattClient.BATTERY_SERVICE)) { + BleBattClient client = (BleBattClient) session.fetchClient(BleBattClient.BATTERY_SERVICE); + return client.waitBatteryLevelUpdate(true).observeOn(scheduler).doOnSuccess(integer -> { + if (callback != null) { + callback.batteryLevelReceived(deviceId, integer); + } + }).toFlowable(); + } else if (uuid.equals(BlePMDClient.PMD_SERVICE)) { + final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE); + return client.waitNotificationEnabled(BlePMDClient.PMD_CP, true). + concatWith(client.waitNotificationEnabled(BlePMDClient.PMD_DATA, true)).andThen(client.readFeature(true).doOnSuccess(pmdFeature -> { if (callback != null) { if (pmdFeature.ecgSupported) { callback.ecgFeatureReady(deviceId); @@ -987,52 +747,31 @@ public void accept(BlePMDClient.PmdFeature pmdFeature) { callback.biozFeatureReady(deviceId); } } - } - })).toFlowable(); - } else if (uuid.equals(BleDisClient.DIS_SERVICE)) { - BleDisClient client = (BleDisClient) session.fetchClient(BleDisClient.DIS_SERVICE); - return client.observeDisInfo(true).observeOn(scheduler).doOnNext(new Consumer>() { - @Override - public void accept(Pair pair) { - if (callback != null) { - callback.disInformationReceived(deviceId, pair.first, pair.second); - } - } - }); - } else if (uuid.equals(BlePsFtpUtils.RFC77_PFTP_SERVICE)) { - BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE); - return client.waitPsFtpClientReady(true).observeOn(scheduler).doOnComplete(new Action() { - @Override - public void run() throws Exception { - if (callback != null && - (session.getPolarDeviceType().equals("OH1") || session.getPolarDeviceType().equals("H10"))) { - callback.polarFtpFeatureReady(deviceId); - } - } - }).toFlowable(); - } + })).toFlowable(); + } else if (uuid.equals(BleDisClient.DIS_SERVICE)) { + BleDisClient client = (BleDisClient) session.fetchClient(BleDisClient.DIS_SERVICE); + return client.observeDisInfo(true).observeOn(scheduler).doOnNext(pair -> { + if (callback != null) { + callback.disInformationReceived(deviceId, pair.first, pair.second); + } + }); + } else if (uuid.equals(BlePsFtpUtils.RFC77_PFTP_SERVICE)) { + BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE); + return client.waitPsFtpClientReady(true).observeOn(scheduler).doOnComplete(() -> { + if (callback != null && + (session.getPolarDeviceType().equals("OH1") || session.getPolarDeviceType().equals("H10"))) { + callback.polarFtpFeatureReady(deviceId); + } + }).toFlowable(); } - return Flowable.empty(); } + return Flowable.empty(); }).subscribe( - new Consumer() { - @Override - public void accept(Object o) throws Exception { + o -> { - } }, - new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - logError(throwable.getMessage()); - } - }, - new Action() { - @Override - public void run() throws Exception { - log("complete"); - } - }); + throwable -> logError(throwable.getMessage()), + () -> log("complete")); } protected Exception handleError(Throwable throwable) { @@ -1088,31 +827,25 @@ protected Flowable fetchRecursively(final BlePsFtpClient client, final S protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder(); builder.setCommand(PftpRequest.PbPFtpOperation.Command.GET); builder.setPath(path); - return client.request(builder.build().toByteArray()).toFlowable().flatMap(new Function>() { - @Override - public Publisher apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception { - PftpResponse.PbPFtpDirectory dir = PftpResponse.PbPFtpDirectory.parseFrom(byteArrayOutputStream.toByteArray()); - Set entrys = new HashSet<>(); - for (int i = 0; i < dir.getEntriesCount(); ++i) { - PftpResponse.PbPFtpEntry entry = dir.getEntries(i); - if (condition.include(entry.getName())) { - BleUtils.validate(entrys.add(path + entry.getName()), "duplicate entry"); - } + return client.request(builder.build().toByteArray()).toFlowable().flatMap((Function>) byteArrayOutputStream -> { + PftpResponse.PbPFtpDirectory dir = PftpResponse.PbPFtpDirectory.parseFrom(byteArrayOutputStream.toByteArray()); + Set entrys = new HashSet<>(); + for (int i = 0; i < dir.getEntriesCount(); ++i) { + PftpResponse.PbPFtpEntry entry = dir.getEntries(i); + if (condition.include(entry.getName())) { + BleUtils.validate(entrys.add(path + entry.getName()), "duplicate entry"); } - if (entrys.size() != 0) { - return Flowable.fromIterable(entrys).flatMap(new Function>() { - @Override - public Publisher apply(String s) { - if (s.endsWith("/")) { - return fetchRecursively(client, s, condition); - } else { - return Flowable.just(s); - } - } - }); - } - return Flowable.empty(); } + if (entrys.size() != 0) { + return Flowable.fromIterable(entrys).flatMap((Function>) s -> { + if (s.endsWith("/")) { + return fetchRecursively(client, s, condition); + } else { + return Flowable.just(s); + } + }); + } + return Flowable.empty(); }); }