diff --git a/demos/Android-Demos/PolarSDK-Broadcast-Demo/app/build.gradle b/demos/Android-Demos/PolarSDK-Broadcast-Demo/app/build.gradle index aef14e93..e493e288 100644 --- a/demos/Android-Demos/PolarSDK-Broadcast-Demo/app/build.gradle +++ b/demos/Android-Demos/PolarSDK-Broadcast-Demo/app/build.gradle @@ -22,6 +22,7 @@ task copySdk { copy { from("$projectDir/../../../../polar-sdk-android/libs") into("$projectDir/libs") + include '*.aar' } } diff --git a/demos/Android-Demos/PolarSDK-ECG-HR-Demo/app/build.gradle b/demos/Android-Demos/PolarSDK-ECG-HR-Demo/app/build.gradle index 2a0b66be..0fc4b96e 100644 --- a/demos/Android-Demos/PolarSDK-ECG-HR-Demo/app/build.gradle +++ b/demos/Android-Demos/PolarSDK-ECG-HR-Demo/app/build.gradle @@ -22,6 +22,7 @@ task copySdk { copy { from("$projectDir/../../../../polar-sdk-android/libs") into("$projectDir/libs") + include '*.aar' } } diff --git a/demos/Android-Demos/PolarSDK-Exercise-Demo/app/build.gradle b/demos/Android-Demos/PolarSDK-Exercise-Demo/app/build.gradle index b65b60b0..b8f91fec 100644 --- a/demos/Android-Demos/PolarSDK-Exercise-Demo/app/build.gradle +++ b/demos/Android-Demos/PolarSDK-Exercise-Demo/app/build.gradle @@ -23,6 +23,7 @@ task copySdk { copy { from("$projectDir/../../../../polar-sdk-android/libs") into("$projectDir/libs") + include '*.aar' } } diff --git a/polar-sdk-android/docs/html/BDBleApiImpl_8java_source.html b/polar-sdk-android/docs/html/BDBleApiImpl_8java_source.html index 391d3720..12ddb36a 100644 --- a/polar-sdk-android/docs/html/BDBleApiImpl_8java_source.html +++ b/polar-sdk-android/docs/html/BDBleApiImpl_8java_source.html @@ -66,9 +66,10 @@
BDBleApiImpl.java
-Go to the documentation of this file.
1 // Copyright © 2019 Polar Electro Oy. All rights reserved.
2 package polar.com.sdk.impl;
3 
4 import android.annotation.SuppressLint;
5 import android.bluetooth.le.ScanFilter;
6 import android.content.Context;
7 import android.os.Build;
8 import android.os.ParcelUuid;
9 import android.support.annotation.Nullable;
10 import android.util.Log;
11 import android.util.Pair;
12 
13 import com.androidcommunications.polar.api.ble.BleDeviceListener;
14 import com.androidcommunications.polar.api.ble.BleLogger;
15 import com.androidcommunications.polar.api.ble.exceptions.BleDisconnected;
16 import com.androidcommunications.polar.api.ble.model.BleDeviceSession;
17 import com.androidcommunications.polar.api.ble.model.advertisement.BleAdvertisementContent;
18 import com.androidcommunications.polar.api.ble.model.advertisement.BlePolarHrAdvertisement;
19 import com.androidcommunications.polar.api.ble.model.gatt.BleGattBase;
20 import com.androidcommunications.polar.api.ble.model.gatt.client.BleBattClient;
21 import com.androidcommunications.polar.api.ble.model.gatt.client.BleDisClient;
22 import com.androidcommunications.polar.api.ble.model.gatt.client.BleHrClient;
23 import com.androidcommunications.polar.api.ble.model.gatt.client.BlePMDClient;
24 import com.androidcommunications.polar.api.ble.model.gatt.client.psftp.BlePsFtpClient;
25 import com.androidcommunications.polar.api.ble.model.gatt.client.psftp.BlePsFtpUtils;
26 import com.androidcommunications.polar.common.ble.BleUtils;
27 import com.androidcommunications.polar.enpoints.ble.bluedroid.host.BDDeviceListenerImpl;
28 
29 import org.reactivestreams.Publisher;
30 
31 import java.io.ByteArrayOutputStream;
32 import java.text.SimpleDateFormat;
33 import java.util.ArrayList;
34 import java.util.Calendar;
35 import java.util.Collections;
36 import java.util.Comparator;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.UUID;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.atomic.AtomicInteger;
47 
48 import fi.polar.remote.representation.protobuf.ExerciseSamples;
49 import fi.polar.remote.representation.protobuf.Types;
50 import io.reactivex.Completable;
51 import io.reactivex.CompletableEmitter;
52 import io.reactivex.CompletableOnSubscribe;
53 import io.reactivex.CompletableSource;
54 import io.reactivex.Flowable;
55 import io.reactivex.Scheduler;
56 import io.reactivex.Single;
57 import io.reactivex.SingleSource;
58 import io.reactivex.android.schedulers.AndroidSchedulers;
59 import io.reactivex.disposables.Disposable;
60 import io.reactivex.functions.Action;
61 import io.reactivex.functions.BiFunction;
62 import io.reactivex.functions.Consumer;
63 import io.reactivex.functions.Function;
64 import io.reactivex.functions.Predicate;
65 import io.reactivex.schedulers.Timed;
66 import polar.com.sdk.api.PolarBleApi;
85 import protocol.PftpNotification;
86 import protocol.PftpRequest;
87 import protocol.PftpResponse;
88 
92 public class BDBleApiImpl extends PolarBleApi {
93  protected final static String TAG = BDBleApiImpl.class.getSimpleName();
94  protected BleDeviceListener listener;
95  protected Map<String,Disposable> connectSubscriptions = new HashMap<>();
96  protected Scheduler scheduler;
99  protected static final int ANDROID_VERSION_O = 26;
100  BleDeviceListener.BleSearchPreFilter filter = new BleDeviceListener.BleSearchPreFilter() {
101  @Override
102  public boolean process(BleAdvertisementContent content) {
103  return content.getPolarDeviceId().length() != 0 && !content.getPolarDeviceType().equals("mobile");
104  }
105  };
106 
107  @SuppressLint({"NewApi", "CheckResult"})
108  public BDBleApiImpl(final Context context, int features) {
109  super(features);
110  Set<Class<? extends BleGattBase>> clients = new HashSet<>();
111  if((this.features & PolarBleApi.FEATURE_HR)!=0){
112  clients.add(BleHrClient.class);
113  }
114  if((this.features & PolarBleApi.FEATURE_DEVICE_INFO)!=0){
115  clients.add(BleDisClient.class);
116  }
117  if((this.features & PolarBleApi.FEATURE_BATTERY_INFO)!=0){
118  clients.add(BleBattClient.class);
119  }
120  if((this.features & PolarBleApi.FEATURE_POLAR_SENSOR_STREAMING)!=0){
121  clients.add(BlePMDClient.class);
122  }
123  if((this.features & PolarBleApi.FEATURE_POLAR_FILE_TRANSFER)!=0){
124  clients.add(BlePsFtpClient.class);
125  }
126  listener = new BDDeviceListenerImpl(context, clients);
127  listener.setScanPreFilter(filter);
128  scheduler = AndroidSchedulers.from(context.getMainLooper());
129  listener.monitorDeviceSessionState(null).observeOn(scheduler).subscribe(
130  new Consumer<Pair<BleDeviceSession, BleDeviceSession.DeviceSessionState>>() {
131  @Override
132  public void accept(Pair<BleDeviceSession, BleDeviceSession.DeviceSessionState> pair) throws Exception {
133  PolarDeviceInfo info = new PolarDeviceInfo(
134  pair.first.getPolarDeviceId().length() != 0 ?
135  pair.first.getPolarDeviceId() : pair.first.getAddress(),
136  pair.first.getAddress(),
137  pair.first.getRssi(),pair.first.getName(),true);
138  switch (pair.second){
139  case SESSION_OPEN:
140  if(callback!=null){
142  }
143  setupDevice(pair.first);
144  break;
145  case SESSION_CLOSED:
146  if( callback != null ) {
147  if (pair.first.getPreviousState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN ||
148  pair.first.getPreviousState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSING){
150  }
151  }
152  break;
153  case SESSION_OPENING:
154  if(callback != null){
156  }
157  break;
158  }
159  }
160  },
161  new Consumer<Throwable>() {
162  @Override
163  public void accept(Throwable throwable) throws Exception {
164  logError(throwable.getMessage());
165  }
166  },
167  new Action() {
168  @Override
169  public void run() throws Exception {
170 
171  }
172  }
173  );
174  listener.monitorBleState().observeOn(scheduler).subscribe(
175  new Consumer<Boolean>() {
176  @Override
177  public void accept(Boolean aBoolean) throws Exception {
178  if(callback != null){
179  callback.blePowerStateChanged(aBoolean);
180  }
181  }
182  },
183  new Consumer<Throwable>() {
184  @Override
185  public void accept(Throwable throwable) throws Exception {
186  logError(throwable.getMessage());
187  }
188  },
189  new Action() {
190  @Override
191  public void run() throws Exception {
192 
193  }
194  }
195  );
196  BleLogger.setLoggerInterface(new BleLogger.BleLoggerInterface() {
197  @Override
198  public void d(String tag, String msg) {
199  log(tag+"/"+msg);
200  }
201 
202  @Override
203  public void e(String tag, String msg) {
204  logError(tag+"/"+msg);
205  }
206 
207  @Override
208  public void w(String tag, String msg) {
209  }
210 
211  @Override
212  public void i(String tag, String msg) {
213  }
214  });
215  }
216 
217  @SuppressLint("NewApi")
218  protected void enableAndroidScanFilter() {
219  if (Build.VERSION.SDK_INT >= ANDROID_VERSION_O) {
220  List<ScanFilter> filter = new ArrayList<>();
221  filter.add(new ScanFilter.Builder().setServiceUuid(
222  ParcelUuid.fromString(BleHrClient.HR_SERVICE.toString())).build());
223  filter.add(new ScanFilter.Builder().setServiceUuid(
224  ParcelUuid.fromString(BlePsFtpUtils.RFC77_PFTP_SERVICE.toString())).build());
225  listener.setScanFilters(filter);
226  }
227  }
228 
229  @Override
230  public void shutDown() {
231  listener.shutDown();
232  }
233 
234  @Override
235  public void cleanup() {
236  listener.removeAllSessions();
237  }
238 
239  @Override
240  public void setPolarFilter(boolean enable) {
241  if(!enable) {
242  listener.setScanPreFilter(null);
243  } else {
244  listener.setScanPreFilter(filter);
245  }
246  }
247 
248  @Override
249  public boolean isFeatureReady(final String deviceId, int feature) {
250  try {
251  switch (feature) {
253  return sessionPsFtpClientReady(deviceId) != null;
255  return sessionPmdClientReady(deviceId) != null;
256  }
257  } catch (Throwable ignored) {
258  }
259  return false;
260  }
261 
262  @Override
264  this.callback = callback;
266  }
267 
268  @Override
269  public void setApiLogger(@Nullable PolarBleApiLogger logger) {
270  this.logger = logger;
271  }
272 
273  @Override
274  public void setAutomaticReconnection(boolean disable) {
275  listener.setAutomaticReconnection(disable);
276  }
277 
278  @Override
279  public Completable setLocalTime(String identifier, Calendar cal) {
280  try {
281  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
282  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
283  PftpRequest.PbPFtpSetLocalTimeParams.Builder builder = PftpRequest.PbPFtpSetLocalTimeParams.newBuilder();
284  Types.PbDate date = Types.PbDate.newBuilder()
285  .setYear(cal.get(Calendar.YEAR))
286  .setMonth(cal.get(Calendar.MONTH) + 1)
287  .setDay(cal.get(Calendar.DAY_OF_MONTH)).build();
288  Types.PbTime time = Types.PbTime.newBuilder()
289  .setHour(cal.get(Calendar.HOUR_OF_DAY))
290  .setMinute(cal.get(Calendar.MINUTE))
291  .setSeconds(cal.get(Calendar.SECOND))
292  .setMillis(cal.get(Calendar.MILLISECOND)).build();
293  builder.setDate(date).setTime(time).setTzOffset((int) TimeUnit.MINUTES.convert(cal.get(Calendar.ZONE_OFFSET), TimeUnit.MILLISECONDS));
294  return client.query(PftpRequest.PbPFtpQuery.SET_LOCAL_TIME_VALUE,builder.build().toByteArray()).toObservable().ignoreElements();
295  } catch (Throwable error){
296  return Completable.error(error);
297  }
298  }
299 
300  @Override
301  public Single<PolarSensorSetting> requestAccSettings(String identifier) {
302  return querySettings(identifier,BlePMDClient.PmdMeasurementType.ACC);
303  }
304 
305  @Override
306  public Single<PolarSensorSetting> requestEcgSettings(String identifier) {
307  return querySettings(identifier,BlePMDClient.PmdMeasurementType.ECG);
308  }
309 
310  @Override
311  public Single<PolarSensorSetting> requestPpgSettings(String identifier) {
312  return querySettings(identifier,BlePMDClient.PmdMeasurementType.PPG);
313  }
314 
315  @Override
316  public Single<PolarSensorSetting> requestBiozSettings(final String identifier){
317  return querySettings(identifier,BlePMDClient.PmdMeasurementType.BIOZ);
318  }
319 
320  protected Single<PolarSensorSetting> querySettings(final String identifier, final BlePMDClient.PmdMeasurementType type) {
321  try {
322  final BleDeviceSession session = sessionPmdClientReady(identifier);
323  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
324  return client.querySettings(type).map(new Function<BlePMDClient.PmdSetting, PolarSensorSetting>() {
325  @Override
326  public PolarSensorSetting apply(BlePMDClient.PmdSetting setting) throws Exception {
327  return new PolarSensorSetting(setting.settings, type);
328  }
329  });
330  } catch (Throwable e){
331  return Single.error(e);
332  }
333  }
334 
335  @Override
336  public void backgroundEntered() {
338  }
339 
340  @Override
341  public void foregroundEntered() {
342  listener.setScanFilters(null);
343  }
344 
345  @Override
346  public Completable autoConnectToDevice(final int rssiLimit, final String service, final int timeout, final TimeUnit unit, final String polarDeviceType) {
347  final long[] start = {0};
348  return Completable.create(new CompletableOnSubscribe() {
349  @Override
350  public void subscribe(CompletableEmitter emitter) throws Exception {
351  if( service == null || service.matches("([0-9a-fA-F]{4})") ) {
352  emitter.onComplete();
353  } else {
354  emitter.tryOnError(new PolarInvalidArgument("Invalid service string format"));
355  }
356  }
357  }).andThen(listener.search(false).filter(new Predicate<BleDeviceSession>() {
358  @Override
359  public boolean test(BleDeviceSession bleDeviceSession) throws Exception {
360  if( bleDeviceSession.getMedianRssi() >= rssiLimit &&
361  bleDeviceSession.isConnectableAdvertisement() &&
362  (polarDeviceType == null || polarDeviceType.equals(bleDeviceSession.getPolarDeviceType())) &&
363  (service == null || bleDeviceSession.getAdvertisementContent().containsService(service)) ) {
364  if(start[0] == 0){
365  start[0] = System.currentTimeMillis();
366  }
367  return true;
368  }
369  return false;
370  }
371  }).timestamp().takeUntil(new Predicate<Timed<BleDeviceSession>>() {
372  @Override
373  public boolean test(Timed<BleDeviceSession> bleDeviceSessionTimed) throws Exception {
374  long diff = bleDeviceSessionTimed.time(TimeUnit.MILLISECONDS) - start[0];
375  return (diff >= unit.toMillis(timeout));
376  }
377  }).reduce(new HashSet<BleDeviceSession>(), new BiFunction<Set<BleDeviceSession>, Timed<BleDeviceSession>, Set<BleDeviceSession>>() {
378  @Override
379  public Set<BleDeviceSession> apply(Set<BleDeviceSession> objects, Timed<BleDeviceSession> bleDeviceSessionTimed) throws Exception {
380  objects.add(bleDeviceSessionTimed.value());
381  return objects;
382  }
383  }).doOnSuccess(new Consumer<Set<BleDeviceSession>>() {
384  @Override
385  public void accept(Set<BleDeviceSession> set) throws Exception {
386  List<BleDeviceSession> list = new ArrayList<>(set);
387  Collections.sort(list, new Comparator<BleDeviceSession>() {
388  @Override
389  public int compare(BleDeviceSession o1, BleDeviceSession o2) {
390  return o1.getRssi() > o2.getRssi() ? -1 : 1;
391  }
392  });
393  listener.openSessionDirect(list.get(0));
394  log("auto connect search complete");
395  }
396  }).toObservable().ignoreElements());
397  }
398 
399  @Override
400  public Completable autoConnectToDevice(final int rssiLimit, final String service, final String polarDeviceType) {
401  return autoConnectToDevice(rssiLimit, service, 2, TimeUnit.SECONDS, polarDeviceType);
402  }
403 
404  @Override
405  public void connectToDevice(final String identifier) throws PolarInvalidArgument {
406  BleDeviceSession session = fetchSession(identifier);
407  if( session == null || session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSED ){
408  if( connectSubscriptions.containsKey(identifier) ){
409  connectSubscriptions.get(identifier).dispose();
410  connectSubscriptions.remove(identifier);
411  }
412  if( session != null ){
413  listener.openSessionDirect(session);
414  } else {
415  connectSubscriptions.put(identifier, listener.search(false).filter(new Predicate<BleDeviceSession>() {
416  @Override
417  public boolean test(BleDeviceSession bleDeviceSession) throws Exception {
418  return identifier.contains(":") ?
419  bleDeviceSession.getAddress().equals(identifier) :
420  bleDeviceSession.getPolarDeviceId().equals(identifier);
421  }
422  }).take(1).observeOn(scheduler).subscribe(
423  new Consumer<BleDeviceSession>() {
424  @Override
425  public void accept(BleDeviceSession bleDeviceSession) throws Exception {
426  listener.openSessionDirect(bleDeviceSession);
427  }
428  },
429  new Consumer<Throwable>() {
430  @Override
431  public void accept(Throwable throwable) throws Exception {
432  logError(throwable.getMessage());
433  }
434  },
435  new Action() {
436  @Override
437  public void run() throws Exception {
438  log("connect search complete");
439  }
440  }
441  ));
442  }
443  }
444  }
445 
446  @Override
447  public void disconnectFromDevice(String identifier) throws PolarInvalidArgument {
448  BleDeviceSession session = fetchSession(identifier);
449  if( session != null ){
450  if( session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN ||
451  session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPENING ||
452  session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK ) {
453  listener.closeSessionDirect(session);
454  }
455  }
456  if (connectSubscriptions.containsKey(identifier)){
457  connectSubscriptions.get(identifier).dispose();
458  connectSubscriptions.remove(identifier);
459  }
460  }
461 
462  @Override
463  public Completable startRecording(String identifier, String exerciseId, RecordingInterval interval, SampleType type) {
464  try {
465  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
466  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
467  if(session.getPolarDeviceType().equals("H10")) {
468  Types.PbSampleType t = type == SampleType.HR ?
469  Types.PbSampleType.SAMPLE_TYPE_HEART_RATE :
470  Types.PbSampleType.SAMPLE_TYPE_RR_INTERVAL;
471  Types.PbDuration duration = Types.PbDuration.newBuilder().setSeconds(interval.getValue()).build();
472  PftpRequest.PbPFtpRequestStartRecordingParams params = PftpRequest.PbPFtpRequestStartRecordingParams.newBuilder().
473  setSampleDataIdentifier(exerciseId).setSampleType(t).setRecordingInterval(duration).build();
474  return client.query(PftpRequest.PbPFtpQuery.REQUEST_START_RECORDING_VALUE, params.toByteArray()).toObservable().ignoreElements().onErrorResumeNext(new Function<Throwable, CompletableSource>() {
475  @Override
476  public CompletableSource apply(Throwable throwable) throws Exception {
477  return Completable.error(throwable);
478  }
479  });
480  }
481  return Completable.error(new PolarOperationNotSupported());
482  } catch (Throwable error){
483  return Completable.error(error);
484  }
485  }
486 
487  @Override
488  public Completable stopRecording(String identifier) {
489  try {
490  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
491  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
492  if(session.getPolarDeviceType().equals("H10")) {
493  return client.query(PftpRequest.PbPFtpQuery.REQUEST_STOP_RECORDING_VALUE, null).toObservable().ignoreElements().onErrorResumeNext(new Function<Throwable, CompletableSource>() {
494  @Override
495  public CompletableSource apply(Throwable throwable) throws Exception {
496  return Completable.error(handleError(throwable));
497  }
498  });
499  }
500  return Completable.error(new PolarOperationNotSupported());
501  } catch (Throwable error){
502  return Completable.error(error);
503  }
504  }
505 
506  @Override
507  public Single<Pair<Boolean,String>> requestRecordingStatus(String identifier) {
508  try {
509  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
510  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
511  if(session.getPolarDeviceType().equals("H10")) {
512  return client.query(PftpRequest.PbPFtpQuery.REQUEST_RECORDING_STATUS_VALUE, null).map(new Function<ByteArrayOutputStream, Pair<Boolean,String>>() {
513  @Override
514  public Pair<Boolean,String> apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception {
515  PftpResponse.PbRequestRecordingStatusResult result = PftpResponse.PbRequestRecordingStatusResult.parseFrom(byteArrayOutputStream.toByteArray());
516  return new Pair<>(result.getRecordingOn(),result.hasSampleDataIdentifier() ? result.getSampleDataIdentifier() : "");
517  }
518  }).onErrorResumeNext(new Function<Throwable, SingleSource<? extends Pair<Boolean, String>>>() {
519  @Override
520  public SingleSource<? extends Pair<Boolean, String>> apply(Throwable throwable) throws Exception {
521  return Single.error(handleError(throwable));
522  }
523  });
524  }
525  return Single.error(new PolarOperationNotSupported());
526  } catch (Throwable error){
527  return Single.error(error);
528  }
529  }
530 
531  @Override
532  public Flowable<PolarExerciseEntry> listExercises(String identifier) {
533  try{
534  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
535  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
536  switch (session.getPolarDeviceType()) {
537  case "OH1":
538  return fetchRecursively(client, "/U/0/", new FetchRecursiveCondition() {
539  @Override
540  public boolean include(String entry) {
541  return entry.matches("^([0-9]{8})(\\/)") ||
542  entry.matches("^([0-9]{6})(\\/)") ||
543  entry.equals("E/") ||
544  entry.equals("SAMPLES.BPB") ||
545  entry.equals("00/");
546  }
547  }).map(new Function<String, PolarExerciseEntry>() {
548  @Override
549  public PolarExerciseEntry apply(String p) throws Exception {
550  String components[] = p.split("/");
551  SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd HHmmss", Locale.getDefault());
552  Date date = format.parse(components[3] + " " + components[5]);
553  return new PolarExerciseEntry(p, date, components[3] + components[5]);
554  }
555  }).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarExerciseEntry>>() {
556  @Override
557  public Publisher<? extends PolarExerciseEntry> apply(Throwable throwable) throws Exception {
558  return Flowable.error(handleError(throwable));
559  }
560  });
561  case "H10":
562  return fetchRecursively(client, "/", new FetchRecursiveCondition() {
563  @Override
564  public boolean include(String entry) {
565  return entry.endsWith("/") || entry.equals("SAMPLES.BPB");
566  }
567  }).map(new Function<String, PolarExerciseEntry>() {
568  @Override
569  public PolarExerciseEntry apply(String p) throws Exception {
570  String components[] = p.split("/");
571  return new PolarExerciseEntry(p, new Date(), components[1]);
572  }
573  }).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarExerciseEntry>>() {
574  @Override
575  public Publisher<? extends PolarExerciseEntry> apply(Throwable throwable) throws Exception {
576  return Flowable.error(handleError(throwable));
577  }
578  });
579  default:
580  return Flowable.error(new PolarOperationNotSupported());
581  }
582  } catch (Throwable error){
583  return Flowable.error(error);
584  }
585  }
586 
587  @Override
588  public Single<PolarExerciseData> fetchExercise(String identifier, PolarExerciseEntry entry) {
589  try{
590  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
591  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
592  protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
593  builder.setCommand(PftpRequest.PbPFtpOperation.Command.GET);
594  builder.setPath(entry.path);
595  if(session.getPolarDeviceType().equals("OH1") || session.getPolarDeviceType().equals("H10")) {
596  return client.request(builder.build().toByteArray()).map(new Function<ByteArrayOutputStream, PolarExerciseData>() {
597  @Override
598  public PolarExerciseData apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception {
599  ExerciseSamples.PbExerciseSamples samples = ExerciseSamples.PbExerciseSamples.parseFrom(byteArrayOutputStream.toByteArray());
600  return new PolarExerciseData(samples.getRecordingInterval().getSeconds(), samples.getHeartRateSamplesList());
601  }
602  }).onErrorResumeNext(new Function<Throwable, SingleSource<? extends PolarExerciseData>>() {
603  @Override
604  public SingleSource<? extends PolarExerciseData> apply(Throwable throwable) throws Exception {
605  return Single.error(handleError(throwable));
606  }
607  });
608  }
609  return Single.error(new PolarOperationNotSupported());
610  } catch (Throwable error){
611  return Single.error(error);
612  }
613  }
614 
615  @Override
616  public Completable removeExercise(String identifier, PolarExerciseEntry entry) {
617  try{
618  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
619  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
620  if(session.getPolarDeviceType().equals("OH1")){
621  protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
622  builder.setCommand(PftpRequest.PbPFtpOperation.Command.GET);
623  final String components[] = entry.path.split("/");
624  final String exerciseParent = "/U/0/" + components[3] + "/E/";
625  builder.setPath(exerciseParent);
626  return client.request(builder.build().toByteArray()).flatMap(new Function<ByteArrayOutputStream, SingleSource<?>>() {
627  @Override
628  public SingleSource<?> apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception {
629  PftpResponse.PbPFtpDirectory directory = PftpResponse.PbPFtpDirectory.parseFrom(byteArrayOutputStream.toByteArray());
630  protocol.PftpRequest.PbPFtpOperation.Builder removeBuilder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
631  removeBuilder.setCommand(PftpRequest.PbPFtpOperation.Command.REMOVE);
632  if( directory.getEntriesCount() <= 1 ){
633  // remove entire directory
634  removeBuilder.setPath("/U/0/" + components[3] + "/");
635  } else {
636  // remove only exercise
637  removeBuilder.setPath("/U/0/" + components[3] + "/E/" + components[5] + "/");
638  }
639  return client.request(removeBuilder.build().toByteArray());
640  }
641  }).toObservable().ignoreElements().onErrorResumeNext(new Function<Throwable, CompletableSource>() {
642  @Override
643  public CompletableSource apply(Throwable throwable) throws Exception {
644  return Completable.error(handleError(throwable));
645  }
646  });
647  } else if(session.getPolarDeviceType().equals("H10")){
648  protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
649  builder.setCommand(PftpRequest.PbPFtpOperation.Command.REMOVE);
650  builder.setPath(entry.path);
651  return client.request(builder.build().toByteArray()).toObservable().ignoreElements().onErrorResumeNext(new Function<Throwable, CompletableSource>() {
652  @Override
653  public CompletableSource apply(Throwable throwable) throws Exception {
654  return Completable.error(handleError(throwable));
655  }
656  });
657  }
658  return Completable.error(new PolarOperationNotSupported());
659  } catch (Throwable error){
660  return Completable.error(error);
661  }
662  }
663 
664  @Override
665  public Flowable<PolarDeviceInfo> searchForDevice() {
666  return listener.search(false).distinct().map(new Function<BleDeviceSession, PolarDeviceInfo>() {
667  @Override
668  public PolarDeviceInfo apply(BleDeviceSession bleDeviceSession) throws Exception {
669  return new PolarDeviceInfo(bleDeviceSession.getPolarDeviceId(),
670  bleDeviceSession.getAddress(),
671  bleDeviceSession.getRssi(),
672  bleDeviceSession.getName(),
673  bleDeviceSession.isConnectableAdvertisement());
674  }
675  });
676  }
677 
678  @Override
679  public Flowable<PolarHrBroadcastData> startListenForPolarHrBroadcasts(final Set<String> deviceIds) {
680  // set filter to null, NOTE this disables reconnection in background
681  return listener.search(false).filter(new Predicate<BleDeviceSession>() {
682  @Override
683  public boolean test(BleDeviceSession bleDeviceSession) throws Exception {
684  return (deviceIds == null || deviceIds.contains(bleDeviceSession.getPolarDeviceId())) &&
685  bleDeviceSession.getAdvertisementContent().getPolarHrAdvertisement().isPresent();
686  }
687  }).map(new Function<BleDeviceSession, PolarHrBroadcastData>() {
688  @Override
689  public PolarHrBroadcastData apply(BleDeviceSession bleDeviceSession) throws Exception {
690  BlePolarHrAdvertisement advertisement = bleDeviceSession.getBlePolarHrAdvertisement();
691  return new PolarHrBroadcastData( new PolarDeviceInfo(bleDeviceSession.getPolarDeviceId(),
692  bleDeviceSession.getAddress(),
693  bleDeviceSession.getRssi(),
694  bleDeviceSession.getName(),
695  bleDeviceSession.isConnectableAdvertisement()),
696  advertisement.getHrForDisplay(),
697  advertisement.getBatteryStatus() != 0);
698  }
699  });
700  }
701 
702  @Override
703  public Flowable<PolarEcgData> startEcgStreaming(String identifier,
704  PolarSensorSetting setting) {
705  try {
706  final BleDeviceSession session = sessionPmdClientReady(identifier);
707  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
708  return client.startMeasurement(BlePMDClient.PmdMeasurementType.ECG, setting.map2PmdSettings()).andThen(
709  client.monitorEcgNotifications(true).map(new Function<BlePMDClient.EcgData, PolarEcgData>() {
710  @Override
711  public PolarEcgData apply(BlePMDClient.EcgData ecgData) throws Exception {
712  List<Integer> samples = new ArrayList<>();
713  for( BlePMDClient.EcgData.EcgSample s : ecgData.ecgSamples ){
714  samples.add(s.microVolts);
715  }
716  return new PolarEcgData(samples,ecgData.timeStamp);
717  }
718  }).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarEcgData>>() {
719  @Override
720  public Publisher<? extends PolarEcgData> apply(Throwable throwable) throws Exception {
721  return Flowable.error(handleError(throwable));
722  }
723  }).doFinally(new Action() {
724  @Override
725  public void run() throws Exception {
726  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.ECG);
727  }
728  }));
729  } catch (Throwable t){
730  return Flowable.error(t);
731  }
732  }
733 
734  @Override
735  public Flowable<PolarAccelerometerData> startAccStreaming(String identifier,
736  PolarSensorSetting setting) {
737  try {
738  final BleDeviceSession session = sessionPmdClientReady(identifier);
739  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
740  return client.startMeasurement(BlePMDClient.PmdMeasurementType.ACC, setting.map2PmdSettings()).andThen(
741  client.monitorAccNotifications(true).map(new Function<BlePMDClient.AccData, PolarAccelerometerData>() {
742  @Override
743  public PolarAccelerometerData apply(BlePMDClient.AccData accData) throws Exception {
744  List<PolarAccelerometerData.PolarAccelerometerSample> samples = new ArrayList<>();
745  for( BlePMDClient.AccData.AccSample s : accData.accSamples ){
746  samples.add(new PolarAccelerometerData.PolarAccelerometerSample(s.x,s.y,s.z));
747  }
748  return new PolarAccelerometerData(samples,accData.timeStamp);
749  }
750  }).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarAccelerometerData>>() {
751  @Override
752  public Publisher<? extends PolarAccelerometerData> apply(Throwable throwable) throws Exception {
753  return Flowable.error(handleError(throwable));
754  }
755  }).doFinally(new Action() {
756  @Override
757  public void run() throws Exception {
758  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.ACC);
759  }
760  }));
761  } catch (Throwable t){
762  return Flowable.error(t);
763  }
764  }
765 
766  @Override
767  public Flowable<PolarOhrPPGData> startOhrPPGStreaming(String identifier,
768  PolarSensorSetting setting) {
769  try {
770  final BleDeviceSession session = sessionPmdClientReady(identifier);
771  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
772  return client.startMeasurement(BlePMDClient.PmdMeasurementType.PPG, setting.map2PmdSettings()).andThen(
773  client.monitorPpgNotifications(true).map(new Function<BlePMDClient.PpgData, PolarOhrPPGData>() {
774  @Override
775  public PolarOhrPPGData apply(BlePMDClient.PpgData ppgData) throws Exception {
776  List<PolarOhrPPGData.PolarOhrPPGSample> samples = new ArrayList<>();
777  for( BlePMDClient.PpgData.PpgSample s : ppgData.ppgSamples ){
778  samples.add(new PolarOhrPPGData.PolarOhrPPGSample(s.ppg0,s.ppg1,s.ppg2,s.ambient));
779  }
780  return new PolarOhrPPGData(samples,ppgData.timeStamp);
781  }
782  }).doFinally(new Action() {
783  @Override
784  public void run() throws Exception {
785  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.PPG);
786  }
787  })).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarOhrPPGData>>() {
788  @Override
789  public Publisher<? extends PolarOhrPPGData> apply(Throwable throwable) throws Exception {
790  return Flowable.error(handleError(throwable));
791  }
792  });
793  } catch (Throwable t){
794  return Flowable.error(t);
795  }
796  }
797 
798  @Override
799  public Flowable<PolarOhrPPIData> startOhrPPIStreaming(String identifier) {
800  try {
801  final BleDeviceSession session = sessionPmdClientReady(identifier);
802  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
803  return client.startMeasurement(BlePMDClient.PmdMeasurementType.PPI, new BlePMDClient.PmdSetting(new HashMap<BlePMDClient.PmdSetting.PmdSettingType, Integer>())).andThen(
804  client.monitorPpiNotifications(true).map(new Function<BlePMDClient.PpiData, PolarOhrPPIData>() {
805  @Override
806  public PolarOhrPPIData apply(BlePMDClient.PpiData ppiData) throws Exception {
807  List<PolarOhrPPIData.PolarOhrPPISample> samples = new ArrayList<>();
808  for(BlePMDClient.PpiData.PPSample ppSample : ppiData.ppSamples){
809  samples.add(new PolarOhrPPIData.PolarOhrPPISample(ppSample.ppInMs,
810  ppSample.ppErrorEstimate,
811  ppSample.hr,
812  ppSample.blockerBit != 0,
813  ppSample.skinContactStatus != 0,
814  ppSample.skinContactSupported != 0));
815  }
816  return new PolarOhrPPIData(ppiData.timestamp,samples);
817  }
818  }).doFinally(new Action() {
819  @Override
820  public void run() throws Exception {
821  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.PPI);
822  }
823  })).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarOhrPPIData>>() {
824  @Override
825  public Publisher<? extends PolarOhrPPIData> apply(Throwable throwable) throws Exception {
826  return Flowable.error(handleError(throwable));
827  }
828  });
829  } catch (Throwable t){
830  return Flowable.error(t);
831  }
832  }
833 
834  @Override
835  public Flowable<PolarBiozData> startBiozStreaming(final String identifier, PolarSensorSetting setting){
836  try {
837  final BleDeviceSession session = sessionPmdClientReady(identifier);
838  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
839  return client.startMeasurement(BlePMDClient.PmdMeasurementType.BIOZ, setting.map2PmdSettings()).andThen(
840  client.monitorBiozNotifications(true).map(new Function<BlePMDClient.BiozData, PolarBiozData>() {
841  @Override
842  public PolarBiozData apply(BlePMDClient.BiozData biozData) throws Exception {
843  return new PolarBiozData(biozData.timeStamp,biozData.samples);
844  }
845  }).doFinally(new Action() {
846  @Override
847  public void run() throws Exception {
848  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.PPG);
849  }
850  })).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarBiozData>>() {
851  @Override
852  public Publisher<? extends PolarBiozData> apply(Throwable throwable) throws Exception {
853  return Flowable.error(handleError(throwable));
854  }
855  });
856  } catch (Throwable t){
857  return Flowable.error(t);
858  }
859  }
860 
861  protected BleDeviceSession fetchSession(final String identifier) throws PolarInvalidArgument {
862  return identifier.contains(":") ?
863  sessionByAddress(identifier) : sessionByDeviceId(identifier);
864  }
865 
866  protected BleDeviceSession sessionByAddress(final String address) throws PolarInvalidArgument {
867  validate(address.matches("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"));
868  for ( BleDeviceSession session : listener.deviceSessions() ){
869  if( session.getAddress().equals(address) ){
870  return session;
871  }
872  }
873  return null;
874  }
875 
876  protected BleDeviceSession sessionByDeviceId(final String deviceId) throws PolarInvalidArgument {
877  validate(deviceId.matches("([0-9a-fA-F]){6,8}"));
878  for ( BleDeviceSession session : listener.deviceSessions() ){
879  if( session.getAdvertisementContent().getPolarDeviceId().equals(deviceId) ){
880  return session;
881  }
882  }
883  return null;
884  }
885 
886  private void validate(boolean a) throws PolarInvalidArgument {
887  if(!a) {
888  throw new PolarInvalidArgument();
889  }
890  }
891 
892  protected BleDeviceSession sessionServiceReady(final String identifier, UUID service) throws Throwable {
893  BleDeviceSession session = fetchSession(identifier);
894  if(session != null){
895  if(session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN) {
896  BleGattBase client = session.fetchClient(service);
897  if (client.isServiceDiscovered()) {
898  return session;
899  }
900  throw new PolarServiceNotAvailable();
901  }
902  throw new PolarDeviceDisconnected();
903  }
904  throw new PolarDeviceNotFound();
905  }
906 
907  public BleDeviceSession sessionPmdClientReady(final String identifier) throws Throwable {
908  BleDeviceSession session = sessionServiceReady(identifier, BlePMDClient.PMD_SERVICE);
909  BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
910  final AtomicInteger pair = client.getNotificationAtomicInteger(BlePMDClient.PMD_CP);
911  final AtomicInteger pairData = client.getNotificationAtomicInteger(BlePMDClient.PMD_DATA);
912  if (pair != null && pairData != null &&
913  pair.get() == BleGattBase.ATT_SUCCESS &&
914  pairData.get() == BleGattBase.ATT_SUCCESS) {
915  return session;
916  }
917  throw new PolarNotificationNotEnabled();
918  }
919 
920  protected BleDeviceSession sessionPsFtpClientReady(final String identifier) throws Throwable {
921  BleDeviceSession session = sessionServiceReady(identifier, BlePsFtpUtils.RFC77_PFTP_SERVICE);
922  BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
923  final AtomicInteger pair = client.getNotificationAtomicInteger(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC);
924  if (pair != null && pair.get() == BleGattBase.ATT_SUCCESS ) {
925  return session;
926  }
927  throw new PolarNotificationNotEnabled();
928  }
929 
930  @SuppressLint("CheckResult")
931  protected void stopPmdStreaming(BleDeviceSession session, BlePMDClient client, BlePMDClient.PmdMeasurementType type) {
932  if( session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN ){
933  // stop streaming
934  client.stopMeasurement(type).subscribe(
935  new Action() {
936  @Override
937  public void run() throws Exception {
938 
939  }
940  },
941  new Consumer<Throwable>() {
942  @Override
943  public void accept(Throwable throwable) throws Exception {
944  logError("failed to stop pmd stream: " + throwable.getLocalizedMessage());
945  }
946  }
947  );
948  }
949  }
950 
951  @SuppressLint("CheckResult")
952  protected void setupDevice(final BleDeviceSession session){
953  final String deviceId = session.getPolarDeviceId().length() != 0 ? session.getPolarDeviceId() : session.getAddress();
954  session.monitorServicesDiscovered(true).observeOn(scheduler).toFlowable().flatMapIterable(
955  new Function<List<UUID>, Iterable<UUID>>() {
956  @Override
957  public Iterable<UUID> apply(List<UUID> uuids) throws Exception {
958  return uuids;
959  }
960  }
961  ).flatMap(new Function<UUID, Publisher<?>>() {
962  @Override
963  public Publisher<?> apply(UUID uuid) throws Exception {
964  if(session.fetchClient(uuid) != null) {
965  if (uuid.equals(BleHrClient.HR_SERVICE)) {
966  if (callback != null) {
967  callback.hrFeatureReady(deviceId);
968  }
969  final BleHrClient client = (BleHrClient) session.fetchClient(BleHrClient.HR_SERVICE);
970  client.observeHrNotifications(true).observeOn(scheduler).subscribe(
971  new Consumer<BleHrClient.HrNotificationData>() {
972  @Override
973  public void accept(BleHrClient.HrNotificationData hrNotificationData) throws Exception {
974  if (callback != null) {
976  new PolarHrData(hrNotificationData.hrValue,
977  hrNotificationData.rrs,
978  hrNotificationData.sensorContact,
979  hrNotificationData.sensorContactSupported,
980  hrNotificationData.rrPresent));
981  }
982  }
983  },
984  new Consumer<Throwable>() {
985  @Override
986  public void accept(Throwable throwable) throws Exception {
987  logError(throwable.getMessage());
988  }
989  },
990  new Action() {
991  @Override
992  public void run() throws Exception {
993 
994  }
995  }
996  );
997  } else if (uuid.equals(BleBattClient.BATTERY_SERVICE)) {
998  BleBattClient client = (BleBattClient) session.fetchClient(BleBattClient.BATTERY_SERVICE);
999  return client.waitBatteryLevelUpdate(true).observeOn(scheduler).doOnSuccess(new Consumer<Integer>() {
1000  @Override
1001  public void accept(Integer integer) throws Exception {
1002  if (callback != null) {
1003  callback.batteryLevelReceived(deviceId, integer);
1004  }
1005  }
1006  }).toFlowable();
1007  } else if (uuid.equals(BlePMDClient.PMD_SERVICE)) {
1008  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
1009  return client.waitNotificationEnabled(BlePMDClient.PMD_CP, true).
1010  concatWith(client.waitNotificationEnabled(BlePMDClient.PMD_DATA, true)).andThen(client.readFeature(true).doOnSuccess(new Consumer<BlePMDClient.PmdFeature>() {
1011  @Override
1012  public void accept(BlePMDClient.PmdFeature pmdFeature) {
1013  if (callback != null) {
1014  if (pmdFeature.ecgSupported) {
1015  callback.ecgFeatureReady(deviceId);
1016  }
1017  if (pmdFeature.accSupported) {
1019  }
1020  if (pmdFeature.ppgSupported) {
1021  callback.ppgFeatureReady(deviceId);
1022  }
1023  if (pmdFeature.ppiSupported) {
1024  callback.ppiFeatureReady(deviceId);
1025  }
1026  if (pmdFeature.bioZSupported) {
1027  callback.biozFeatureReady(deviceId);
1028  }
1029  }
1030  }
1031  })).toFlowable();
1032  } else if (uuid.equals(BleDisClient.DIS_SERVICE)) {
1033  BleDisClient client = (BleDisClient) session.fetchClient(BleDisClient.DIS_SERVICE);
1034  return client.observeDisInfo(true).observeOn(scheduler).doOnNext(new Consumer<Pair<UUID, String>>() {
1035  @Override
1036  public void accept(Pair<UUID, String> pair) {
1037  if (callback != null) {
1038  callback.disInformationReceived(deviceId, pair.first , pair.second);
1039  }
1040  }
1041  });
1042  } else if (uuid.equals(BlePsFtpUtils.RFC77_PFTP_SERVICE)) {
1043  BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
1044  return client.waitPsFtpClientReady(true).observeOn(scheduler).doOnComplete(new Action() {
1045  @Override
1046  public void run() throws Exception {
1047  if (callback != null &&
1048  (session.getPolarDeviceType().equals("OH1") || session.getPolarDeviceType().equals("H10"))) {
1049  callback.polarFtpFeatureReady(deviceId);
1050  }
1051  }
1052  }).toFlowable();
1053  }
1054  }
1055  return Flowable.empty();
1056  }
1057  }).subscribe(
1058  new Consumer<Object>() {
1059  @Override
1060  public void accept(Object o) throws Exception {
1061 
1062  }
1063  },
1064  new Consumer<Throwable>() {
1065  @Override
1066  public void accept(Throwable throwable) throws Exception {
1067  logError(throwable.getMessage());
1068  }
1069  },
1070  new Action() {
1071  @Override
1072  public void run() throws Exception {
1073  log("complete");
1074  }
1075  });
1076  }
1077 
1078  protected Exception handleError(Throwable throwable) {
1079  if( throwable instanceof BleDisconnected ){
1080  return new PolarDeviceDisconnected();
1081  } else {
1082  return new Exception("Unknown Error: " + throwable.getLocalizedMessage());
1083  }
1084  }
1085 
1086  interface FetchRecursiveCondition {
1087  boolean include(String entry);
1088  }
1089 
1090  protected Flowable<String> fetchRecursively(final BlePsFtpClient client, final String path, final FetchRecursiveCondition condition) {
1091  protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
1092  builder.setCommand(PftpRequest.PbPFtpOperation.Command.GET);
1093  builder.setPath(path);
1094  return client.request(builder.build().toByteArray()).toFlowable().flatMap(new Function<ByteArrayOutputStream, Publisher<String>>() {
1095  @Override
1096  public Publisher<String> apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception {
1097  PftpResponse.PbPFtpDirectory dir = PftpResponse.PbPFtpDirectory.parseFrom(byteArrayOutputStream.toByteArray());
1098  Set<String> entrys = new HashSet<>();
1099  for( int i=0; i < dir.getEntriesCount(); ++i ){
1100  PftpResponse.PbPFtpEntry entry = dir.getEntries(i);
1101  if( condition.include(entry.getName()) ){
1102  BleUtils.validate(entrys.add(path + entry.getName()),"duplicate entry");
1103  }
1104  }
1105  if(entrys.size()!=0) {
1106  return Flowable.fromIterable(entrys).flatMap(new Function<String, Publisher<String>>() {
1107  @Override
1108  public Publisher<String> apply(String s) {
1109  if (s.endsWith("/")) {
1110  return fetchRecursively(client, s, condition);
1111  } else {
1112  return Flowable.just(s);
1113  }
1114  }
1115  });
1116  }
1117  return Flowable.empty();
1118  }
1119  });
1120  }
1121 
1122  protected void log(final String message) {
1123  if(logger != null){
1124  logger.message("" + message);
1125  }
1126  }
1127 
1128  protected void logError(final String message) {
1129  if(logger != null){
1130  logger.message("Error: "+message);
1131  }
1132  }
1133 }
-
void log(final String message)
+Go to the documentation of this file.
1 // Copyright © 2019 Polar Electro Oy. All rights reserved.
2 package polar.com.sdk.impl;
3 
4 import android.annotation.SuppressLint;
5 import android.bluetooth.le.ScanFilter;
6 import android.content.Context;
7 import android.os.Build;
8 import android.os.ParcelUuid;
9 import android.support.annotation.Nullable;
10 import android.util.Log;
11 import android.util.Pair;
12 
13 import com.androidcommunications.polar.api.ble.BleDeviceListener;
14 import com.androidcommunications.polar.api.ble.BleLogger;
15 import com.androidcommunications.polar.api.ble.exceptions.BleDisconnected;
16 import com.androidcommunications.polar.api.ble.model.BleDeviceSession;
17 import com.androidcommunications.polar.api.ble.model.advertisement.BleAdvertisementContent;
18 import com.androidcommunications.polar.api.ble.model.advertisement.BlePolarHrAdvertisement;
19 import com.androidcommunications.polar.api.ble.model.gatt.BleGattBase;
20 import com.androidcommunications.polar.api.ble.model.gatt.client.BleBattClient;
21 import com.androidcommunications.polar.api.ble.model.gatt.client.BleDisClient;
22 import com.androidcommunications.polar.api.ble.model.gatt.client.BleHrClient;
23 import com.androidcommunications.polar.api.ble.model.gatt.client.BlePMDClient;
24 import com.androidcommunications.polar.api.ble.model.gatt.client.psftp.BlePsFtpClient;
25 import com.androidcommunications.polar.api.ble.model.gatt.client.psftp.BlePsFtpUtils;
26 import com.androidcommunications.polar.common.ble.BleUtils;
27 import com.androidcommunications.polar.enpoints.ble.bluedroid.host.BDDeviceListenerImpl;
28 
29 import org.reactivestreams.Publisher;
30 
31 import java.io.ByteArrayOutputStream;
32 import java.text.SimpleDateFormat;
33 import java.util.ArrayList;
34 import java.util.Calendar;
35 import java.util.Collections;
36 import java.util.Comparator;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.UUID;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.atomic.AtomicInteger;
47 
48 import fi.polar.remote.representation.protobuf.ExerciseSamples;
49 import fi.polar.remote.representation.protobuf.Types;
50 import io.reactivex.Completable;
51 import io.reactivex.CompletableEmitter;
52 import io.reactivex.CompletableOnSubscribe;
53 import io.reactivex.CompletableSource;
54 import io.reactivex.Flowable;
55 import io.reactivex.Scheduler;
56 import io.reactivex.Single;
57 import io.reactivex.SingleSource;
58 import io.reactivex.android.schedulers.AndroidSchedulers;
59 import io.reactivex.disposables.Disposable;
60 import io.reactivex.functions.Action;
61 import io.reactivex.functions.BiFunction;
62 import io.reactivex.functions.Consumer;
63 import io.reactivex.functions.Function;
64 import io.reactivex.functions.Predicate;
65 import io.reactivex.schedulers.Timed;
66 import polar.com.sdk.api.PolarBleApi;
85 import protocol.PftpNotification;
86 import protocol.PftpRequest;
87 import protocol.PftpResponse;
88 
92 public class BDBleApiImpl extends PolarBleApi {
93  protected final static String TAG = BDBleApiImpl.class.getSimpleName();
94  protected BleDeviceListener listener;
95  protected Map<String,Disposable> connectSubscriptions = new HashMap<>();
96  protected Scheduler scheduler;
99  protected static final int ANDROID_VERSION_O = 26;
100  BleDeviceListener.BleSearchPreFilter filter = new BleDeviceListener.BleSearchPreFilter() {
101  @Override
102  public boolean process(BleAdvertisementContent content) {
103  return content.getPolarDeviceId().length() != 0 && !content.getPolarDeviceType().equals("mobile");
104  }
105  };
106 
107  @SuppressLint({"NewApi", "CheckResult"})
108  public BDBleApiImpl(final Context context, int features) {
109  super(features);
110  Set<Class<? extends BleGattBase>> clients = new HashSet<>();
111  if((this.features & PolarBleApi.FEATURE_HR)!=0){
112  clients.add(BleHrClient.class);
113  }
114  if((this.features & PolarBleApi.FEATURE_DEVICE_INFO)!=0){
115  clients.add(BleDisClient.class);
116  }
117  if((this.features & PolarBleApi.FEATURE_BATTERY_INFO)!=0){
118  clients.add(BleBattClient.class);
119  }
120  if((this.features & PolarBleApi.FEATURE_POLAR_SENSOR_STREAMING)!=0){
121  clients.add(BlePMDClient.class);
122  }
123  if((this.features & PolarBleApi.FEATURE_POLAR_FILE_TRANSFER)!=0){
124  clients.add(BlePsFtpClient.class);
125  }
126  listener = new BDDeviceListenerImpl(context, clients);
127  listener.setScanPreFilter(filter);
128  scheduler = AndroidSchedulers.from(context.getMainLooper());
129  listener.monitorDeviceSessionState(null).observeOn(scheduler).subscribe(
130  new Consumer<Pair<BleDeviceSession, BleDeviceSession.DeviceSessionState>>() {
131  @Override
132  public void accept(Pair<BleDeviceSession, BleDeviceSession.DeviceSessionState> pair) throws Exception {
133  PolarDeviceInfo info = new PolarDeviceInfo(
134  pair.first.getPolarDeviceId().length() != 0 ?
135  pair.first.getPolarDeviceId() : pair.first.getAddress(),
136  pair.first.getAddress(),
137  pair.first.getRssi(),pair.first.getName(),true);
138  switch (pair.second){
139  case SESSION_OPEN:
140  if(callback!=null){
142  }
143  setupDevice(pair.first);
144  break;
145  case SESSION_CLOSED:
146  if( callback != null ) {
147  if (pair.first.getPreviousState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN ||
148  pair.first.getPreviousState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSING){
150  }
151  }
152  break;
153  case SESSION_OPENING:
154  if(callback != null){
156  }
157  break;
158  }
159  }
160  },
161  new Consumer<Throwable>() {
162  @Override
163  public void accept(Throwable throwable) throws Exception {
164  logError(throwable.getMessage());
165  }
166  },
167  new Action() {
168  @Override
169  public void run() throws Exception {
170 
171  }
172  }
173  );
174  listener.monitorBleState().observeOn(scheduler).subscribe(
175  new Consumer<Boolean>() {
176  @Override
177  public void accept(Boolean aBoolean) throws Exception {
178  if(callback != null){
179  callback.blePowerStateChanged(aBoolean);
180  }
181  }
182  },
183  new Consumer<Throwable>() {
184  @Override
185  public void accept(Throwable throwable) throws Exception {
186  logError(throwable.getMessage());
187  }
188  },
189  new Action() {
190  @Override
191  public void run() throws Exception {
192 
193  }
194  }
195  );
196  BleLogger.setLoggerInterface(new BleLogger.BleLoggerInterface() {
197  @Override
198  public void d(String tag, String msg) {
199  log(tag+"/"+msg);
200  }
201 
202  @Override
203  public void e(String tag, String msg) {
204  logError(tag+"/"+msg);
205  }
206 
207  @Override
208  public void w(String tag, String msg) {
209  }
210 
211  @Override
212  public void i(String tag, String msg) {
213  }
214  });
215  }
216 
217  @SuppressLint("NewApi")
218  protected void enableAndroidScanFilter() {
219  if (Build.VERSION.SDK_INT >= ANDROID_VERSION_O) {
220  List<ScanFilter> filter = new ArrayList<>();
221  filter.add(new ScanFilter.Builder().setServiceUuid(
222  ParcelUuid.fromString(BleHrClient.HR_SERVICE.toString())).build());
223  filter.add(new ScanFilter.Builder().setServiceUuid(
224  ParcelUuid.fromString(BlePsFtpUtils.RFC77_PFTP_SERVICE.toString())).build());
225  listener.setScanFilters(filter);
226  }
227  }
228 
229  @Override
230  public void shutDown() {
231  listener.shutDown();
232  }
233 
234  @Override
235  public void cleanup() {
236  listener.removeAllSessions();
237  }
238 
239  @Override
240  public void setPolarFilter(boolean enable) {
241  if(!enable) {
242  listener.setScanPreFilter(null);
243  } else {
244  listener.setScanPreFilter(filter);
245  }
246  }
247 
248  @Override
249  public boolean isFeatureReady(final String deviceId, int feature) {
250  try {
251  switch (feature) {
253  return sessionPsFtpClientReady(deviceId) != null;
255  return sessionPmdClientReady(deviceId) != null;
256  }
257  } catch (Throwable ignored) {
258  }
259  return false;
260  }
261 
262  @Override
264  this.callback = callback;
266  }
267 
268  @Override
269  public void setApiLogger(@Nullable PolarBleApiLogger logger) {
270  this.logger = logger;
271  }
272 
273  @Override
274  public void setAutomaticReconnection(boolean disable) {
275  listener.setAutomaticReconnection(disable);
276  }
277 
278  @Override
279  public Completable setLocalTime(String identifier, Calendar cal) {
280  try {
281  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
282  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
283  PftpRequest.PbPFtpSetLocalTimeParams.Builder builder = PftpRequest.PbPFtpSetLocalTimeParams.newBuilder();
284  Types.PbDate date = Types.PbDate.newBuilder()
285  .setYear(cal.get(Calendar.YEAR))
286  .setMonth(cal.get(Calendar.MONTH) + 1)
287  .setDay(cal.get(Calendar.DAY_OF_MONTH)).build();
288  Types.PbTime time = Types.PbTime.newBuilder()
289  .setHour(cal.get(Calendar.HOUR_OF_DAY))
290  .setMinute(cal.get(Calendar.MINUTE))
291  .setSeconds(cal.get(Calendar.SECOND))
292  .setMillis(cal.get(Calendar.MILLISECOND)).build();
293  builder.setDate(date).setTime(time).setTzOffset((int) TimeUnit.MINUTES.convert(cal.get(Calendar.ZONE_OFFSET), TimeUnit.MILLISECONDS));
294  return client.query(PftpRequest.PbPFtpQuery.SET_LOCAL_TIME_VALUE,builder.build().toByteArray()).toObservable().ignoreElements();
295  } catch (Throwable error){
296  return Completable.error(error);
297  }
298  }
299 
300  @Override
301  public Single<PolarSensorSetting> requestAccSettings(String identifier) {
302  return querySettings(identifier,BlePMDClient.PmdMeasurementType.ACC);
303  }
304 
305  @Override
306  public Single<PolarSensorSetting> requestEcgSettings(String identifier) {
307  return querySettings(identifier,BlePMDClient.PmdMeasurementType.ECG);
308  }
309 
310  @Override
311  public Single<PolarSensorSetting> requestPpgSettings(String identifier) {
312  return querySettings(identifier,BlePMDClient.PmdMeasurementType.PPG);
313  }
314 
315  @Override
316  public Single<PolarSensorSetting> requestBiozSettings(final String identifier){
317  return querySettings(identifier,BlePMDClient.PmdMeasurementType.BIOZ);
318  }
319 
320  protected Single<PolarSensorSetting> querySettings(final String identifier, final BlePMDClient.PmdMeasurementType type) {
321  try {
322  final BleDeviceSession session = sessionPmdClientReady(identifier);
323  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
324  return client.querySettings(type).map(new Function<BlePMDClient.PmdSetting, PolarSensorSetting>() {
325  @Override
326  public PolarSensorSetting apply(BlePMDClient.PmdSetting setting) throws Exception {
327  return new PolarSensorSetting(setting.settings, type);
328  }
329  });
330  } catch (Throwable e){
331  return Single.error(e);
332  }
333  }
334 
335  @Override
336  public void backgroundEntered() {
338  }
339 
340  @Override
341  public void foregroundEntered() {
342  listener.setScanFilters(null);
343  }
344 
345  @Override
346  public Completable autoConnectToDevice(final int rssiLimit, final String service, final int timeout, final TimeUnit unit, final String polarDeviceType) {
347  final long[] start = {0};
348  return Completable.create(new CompletableOnSubscribe() {
349  @Override
350  public void subscribe(CompletableEmitter emitter) throws Exception {
351  if( service == null || service.matches("([0-9a-fA-F]{4})") ) {
352  emitter.onComplete();
353  } else {
354  emitter.tryOnError(new PolarInvalidArgument("Invalid service string format"));
355  }
356  }
357  }).andThen(listener.search(false).filter(new Predicate<BleDeviceSession>() {
358  @Override
359  public boolean test(BleDeviceSession bleDeviceSession) throws Exception {
360  if( bleDeviceSession.getMedianRssi() >= rssiLimit &&
361  bleDeviceSession.isConnectableAdvertisement() &&
362  (polarDeviceType == null || polarDeviceType.equals(bleDeviceSession.getPolarDeviceType())) &&
363  (service == null || bleDeviceSession.getAdvertisementContent().containsService(service)) ) {
364  if(start[0] == 0){
365  start[0] = System.currentTimeMillis();
366  }
367  return true;
368  }
369  return false;
370  }
371  }).timestamp().takeUntil(new Predicate<Timed<BleDeviceSession>>() {
372  @Override
373  public boolean test(Timed<BleDeviceSession> bleDeviceSessionTimed) throws Exception {
374  long diff = bleDeviceSessionTimed.time(TimeUnit.MILLISECONDS) - start[0];
375  return (diff >= unit.toMillis(timeout));
376  }
377  }).reduce(new HashSet<BleDeviceSession>(), new BiFunction<Set<BleDeviceSession>, Timed<BleDeviceSession>, Set<BleDeviceSession>>() {
378  @Override
379  public Set<BleDeviceSession> apply(Set<BleDeviceSession> objects, Timed<BleDeviceSession> bleDeviceSessionTimed) throws Exception {
380  objects.add(bleDeviceSessionTimed.value());
381  return objects;
382  }
383  }).doOnSuccess(new Consumer<Set<BleDeviceSession>>() {
384  @Override
385  public void accept(Set<BleDeviceSession> set) throws Exception {
386  List<BleDeviceSession> list = new ArrayList<>(set);
387  Collections.sort(list, new Comparator<BleDeviceSession>() {
388  @Override
389  public int compare(BleDeviceSession o1, BleDeviceSession o2) {
390  return o1.getRssi() > o2.getRssi() ? -1 : 1;
391  }
392  });
393  listener.openSessionDirect(list.get(0));
394  log("auto connect search complete");
395  }
396  }).toObservable().ignoreElements());
397  }
398 
399  @Override
400  public Completable autoConnectToDevice(final int rssiLimit, final String service, final String polarDeviceType) {
401  return autoConnectToDevice(rssiLimit, service, 2, TimeUnit.SECONDS, polarDeviceType);
402  }
403 
404  @Override
405  public void connectToDevice(final String identifier) throws PolarInvalidArgument {
406  BleDeviceSession session = fetchSession(identifier);
407  if( session == null || session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_CLOSED ){
408  if( connectSubscriptions.containsKey(identifier) ){
409  connectSubscriptions.get(identifier).dispose();
410  connectSubscriptions.remove(identifier);
411  }
412  if( session != null ){
413  listener.openSessionDirect(session);
414  } else {
415  connectSubscriptions.put(identifier, listener.search(false).filter(new Predicate<BleDeviceSession>() {
416  @Override
417  public boolean test(BleDeviceSession bleDeviceSession) throws Exception {
418  return identifier.contains(":") ?
419  bleDeviceSession.getAddress().equals(identifier) :
420  bleDeviceSession.getPolarDeviceId().equals(identifier);
421  }
422  }).take(1).observeOn(scheduler).subscribe(
423  new Consumer<BleDeviceSession>() {
424  @Override
425  public void accept(BleDeviceSession bleDeviceSession) throws Exception {
426  listener.openSessionDirect(bleDeviceSession);
427  }
428  },
429  new Consumer<Throwable>() {
430  @Override
431  public void accept(Throwable throwable) throws Exception {
432  logError(throwable.getMessage());
433  }
434  },
435  new Action() {
436  @Override
437  public void run() throws Exception {
438  log("connect search complete");
439  }
440  }
441  ));
442  }
443  }
444  }
445 
446  @Override
447  public void disconnectFromDevice(String identifier) throws PolarInvalidArgument {
448  BleDeviceSession session = fetchSession(identifier);
449  if( session != null ){
450  if( session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN ||
451  session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPENING ||
452  session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN_PARK ) {
453  listener.closeSessionDirect(session);
454  }
455  }
456  if (connectSubscriptions.containsKey(identifier)){
457  connectSubscriptions.get(identifier).dispose();
458  connectSubscriptions.remove(identifier);
459  }
460  }
461 
462  @Override
463  public Completable startRecording(String identifier, String exerciseId, RecordingInterval interval, SampleType type) {
464  try {
465  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
466  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
467  if(session.getPolarDeviceType().equals("H10")) {
468  Types.PbSampleType t = type == SampleType.HR ?
469  Types.PbSampleType.SAMPLE_TYPE_HEART_RATE :
470  Types.PbSampleType.SAMPLE_TYPE_RR_INTERVAL;
471  Types.PbDuration duration = Types.PbDuration.newBuilder().setSeconds(interval.getValue()).build();
472  PftpRequest.PbPFtpRequestStartRecordingParams params = PftpRequest.PbPFtpRequestStartRecordingParams.newBuilder().
473  setSampleDataIdentifier(exerciseId).setSampleType(t).setRecordingInterval(duration).build();
474  return client.query(PftpRequest.PbPFtpQuery.REQUEST_START_RECORDING_VALUE, params.toByteArray()).toObservable().ignoreElements().onErrorResumeNext(new Function<Throwable, CompletableSource>() {
475  @Override
476  public CompletableSource apply(Throwable throwable) throws Exception {
477  return Completable.error(throwable);
478  }
479  });
480  }
481  return Completable.error(new PolarOperationNotSupported());
482  } catch (Throwable error){
483  return Completable.error(error);
484  }
485  }
486 
487  @Override
488  public Completable stopRecording(String identifier) {
489  try {
490  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
491  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
492  if(session.getPolarDeviceType().equals("H10")) {
493  return client.query(PftpRequest.PbPFtpQuery.REQUEST_STOP_RECORDING_VALUE, null).toObservable().ignoreElements().onErrorResumeNext(new Function<Throwable, CompletableSource>() {
494  @Override
495  public CompletableSource apply(Throwable throwable) throws Exception {
496  return Completable.error(handleError(throwable));
497  }
498  });
499  }
500  return Completable.error(new PolarOperationNotSupported());
501  } catch (Throwable error){
502  return Completable.error(error);
503  }
504  }
505 
506  @Override
507  public Single<Pair<Boolean,String>> requestRecordingStatus(String identifier) {
508  try {
509  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
510  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
511  if(session.getPolarDeviceType().equals("H10")) {
512  return client.query(PftpRequest.PbPFtpQuery.REQUEST_RECORDING_STATUS_VALUE, null).map(new Function<ByteArrayOutputStream, Pair<Boolean,String>>() {
513  @Override
514  public Pair<Boolean,String> apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception {
515  PftpResponse.PbRequestRecordingStatusResult result = PftpResponse.PbRequestRecordingStatusResult.parseFrom(byteArrayOutputStream.toByteArray());
516  return new Pair<>(result.getRecordingOn(),result.hasSampleDataIdentifier() ? result.getSampleDataIdentifier() : "");
517  }
518  }).onErrorResumeNext(new Function<Throwable, SingleSource<? extends Pair<Boolean, String>>>() {
519  @Override
520  public SingleSource<? extends Pair<Boolean, String>> apply(Throwable throwable) throws Exception {
521  return Single.error(handleError(throwable));
522  }
523  });
524  }
525  return Single.error(new PolarOperationNotSupported());
526  } catch (Throwable error){
527  return Single.error(error);
528  }
529  }
530 
531  @Override
532  public Flowable<PolarExerciseEntry> listExercises(String identifier) {
533  try{
534  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
535  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
536  switch (session.getPolarDeviceType()) {
537  case "OH1":
538  return fetchRecursively(client, "/U/0/", new FetchRecursiveCondition() {
539  @Override
540  public boolean include(String entry) {
541  return entry.matches("^([0-9]{8})(\\/)") ||
542  entry.matches("^([0-9]{6})(\\/)") ||
543  entry.equals("E/") ||
544  entry.equals("SAMPLES.BPB") ||
545  entry.equals("00/");
546  }
547  }).map(new Function<String, PolarExerciseEntry>() {
548  @Override
549  public PolarExerciseEntry apply(String p) throws Exception {
550  String components[] = p.split("/");
551  SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd HHmmss", Locale.getDefault());
552  Date date = format.parse(components[3] + " " + components[5]);
553  return new PolarExerciseEntry(p, date, components[3] + components[5]);
554  }
555  }).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarExerciseEntry>>() {
556  @Override
557  public Publisher<? extends PolarExerciseEntry> apply(Throwable throwable) throws Exception {
558  return Flowable.error(handleError(throwable));
559  }
560  });
561  case "H10":
562  return fetchRecursively(client, "/", new FetchRecursiveCondition() {
563  @Override
564  public boolean include(String entry) {
565  return entry.endsWith("/") || entry.equals("SAMPLES.BPB");
566  }
567  }).map(new Function<String, PolarExerciseEntry>() {
568  @Override
569  public PolarExerciseEntry apply(String p) throws Exception {
570  String components[] = p.split("/");
571  return new PolarExerciseEntry(p, new Date(), components[1]);
572  }
573  }).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarExerciseEntry>>() {
574  @Override
575  public Publisher<? extends PolarExerciseEntry> apply(Throwable throwable) throws Exception {
576  return Flowable.error(handleError(throwable));
577  }
578  });
579  default:
580  return Flowable.error(new PolarOperationNotSupported());
581  }
582  } catch (Throwable error){
583  return Flowable.error(error);
584  }
585  }
586 
587  @Override
588  public Single<PolarExerciseData> fetchExercise(String identifier, PolarExerciseEntry entry) {
589  try{
590  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
591  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
592  protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
593  builder.setCommand(PftpRequest.PbPFtpOperation.Command.GET);
594  builder.setPath(entry.path);
595  if(session.getPolarDeviceType().equals("OH1") || session.getPolarDeviceType().equals("H10")) {
596  return client.request(builder.build().toByteArray()).map(new Function<ByteArrayOutputStream, PolarExerciseData>() {
597  @Override
598  public PolarExerciseData apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception {
599  ExerciseSamples.PbExerciseSamples samples = ExerciseSamples.PbExerciseSamples.parseFrom(byteArrayOutputStream.toByteArray());
600  return new PolarExerciseData(samples.getRecordingInterval().getSeconds(), samples.getHeartRateSamplesList());
601  }
602  }).onErrorResumeNext(new Function<Throwable, SingleSource<? extends PolarExerciseData>>() {
603  @Override
604  public SingleSource<? extends PolarExerciseData> apply(Throwable throwable) throws Exception {
605  return Single.error(handleError(throwable));
606  }
607  });
608  }
609  return Single.error(new PolarOperationNotSupported());
610  } catch (Throwable error){
611  return Single.error(error);
612  }
613  }
614 
615  @Override
616  public Completable removeExercise(String identifier, PolarExerciseEntry entry) {
617  try{
618  final BleDeviceSession session = sessionPsFtpClientReady(identifier);
619  final BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
620  if(session.getPolarDeviceType().equals("OH1")){
621  protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
622  builder.setCommand(PftpRequest.PbPFtpOperation.Command.GET);
623  final String components[] = entry.path.split("/");
624  final String exerciseParent = "/U/0/" + components[3] + "/E/";
625  builder.setPath(exerciseParent);
626  return client.request(builder.build().toByteArray()).flatMap(new Function<ByteArrayOutputStream, SingleSource<?>>() {
627  @Override
628  public SingleSource<?> apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception {
629  PftpResponse.PbPFtpDirectory directory = PftpResponse.PbPFtpDirectory.parseFrom(byteArrayOutputStream.toByteArray());
630  protocol.PftpRequest.PbPFtpOperation.Builder removeBuilder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
631  removeBuilder.setCommand(PftpRequest.PbPFtpOperation.Command.REMOVE);
632  if( directory.getEntriesCount() <= 1 ){
633  // remove entire directory
634  removeBuilder.setPath("/U/0/" + components[3] + "/");
635  } else {
636  // remove only exercise
637  removeBuilder.setPath("/U/0/" + components[3] + "/E/" + components[5] + "/");
638  }
639  return client.request(removeBuilder.build().toByteArray());
640  }
641  }).toObservable().ignoreElements().onErrorResumeNext(new Function<Throwable, CompletableSource>() {
642  @Override
643  public CompletableSource apply(Throwable throwable) throws Exception {
644  return Completable.error(handleError(throwable));
645  }
646  });
647  } else if(session.getPolarDeviceType().equals("H10")){
648  protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
649  builder.setCommand(PftpRequest.PbPFtpOperation.Command.REMOVE);
650  builder.setPath(entry.path);
651  return client.request(builder.build().toByteArray()).toObservable().ignoreElements().onErrorResumeNext(new Function<Throwable, CompletableSource>() {
652  @Override
653  public CompletableSource apply(Throwable throwable) throws Exception {
654  return Completable.error(handleError(throwable));
655  }
656  });
657  }
658  return Completable.error(new PolarOperationNotSupported());
659  } catch (Throwable error){
660  return Completable.error(error);
661  }
662  }
663 
664  @Override
665  public Flowable<PolarDeviceInfo> searchForDevice() {
666  return listener.search(false).distinct().map(new Function<BleDeviceSession, PolarDeviceInfo>() {
667  @Override
668  public PolarDeviceInfo apply(BleDeviceSession bleDeviceSession) throws Exception {
669  return new PolarDeviceInfo(bleDeviceSession.getPolarDeviceId(),
670  bleDeviceSession.getAddress(),
671  bleDeviceSession.getRssi(),
672  bleDeviceSession.getName(),
673  bleDeviceSession.isConnectableAdvertisement());
674  }
675  });
676  }
677 
678  @Override
679  public Flowable<PolarHrBroadcastData> startListenForPolarHrBroadcasts(final Set<String> deviceIds) {
680  // set filter to null, NOTE this disables reconnection in background
681  return listener.search(false).filter(new Predicate<BleDeviceSession>() {
682  @Override
683  public boolean test(BleDeviceSession bleDeviceSession) throws Exception {
684  return (deviceIds == null || deviceIds.contains(bleDeviceSession.getPolarDeviceId())) &&
685  bleDeviceSession.getAdvertisementContent().getPolarHrAdvertisement().isPresent();
686  }
687  }).map(new Function<BleDeviceSession, PolarHrBroadcastData>() {
688  @Override
689  public PolarHrBroadcastData apply(BleDeviceSession bleDeviceSession) throws Exception {
690  BlePolarHrAdvertisement advertisement = bleDeviceSession.getBlePolarHrAdvertisement();
691  return new PolarHrBroadcastData( new PolarDeviceInfo(bleDeviceSession.getPolarDeviceId(),
692  bleDeviceSession.getAddress(),
693  bleDeviceSession.getRssi(),
694  bleDeviceSession.getName(),
695  bleDeviceSession.isConnectableAdvertisement()),
696  advertisement.getHrForDisplay(),
697  advertisement.getBatteryStatus() != 0);
698  }
699  });
700  }
701 
702  @Override
703  public Flowable<PolarEcgData> startEcgStreaming(String identifier,
704  PolarSensorSetting setting) {
705  try {
706  final BleDeviceSession session = sessionPmdClientReady(identifier);
707  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
708  return client.startMeasurement(BlePMDClient.PmdMeasurementType.ECG, setting.map2PmdSettings()).andThen(
709  client.monitorEcgNotifications(true).map(new Function<BlePMDClient.EcgData, PolarEcgData>() {
710  @Override
711  public PolarEcgData apply(BlePMDClient.EcgData ecgData) throws Exception {
712  List<Integer> samples = new ArrayList<>();
713  for( BlePMDClient.EcgData.EcgSample s : ecgData.ecgSamples ){
714  samples.add(s.microVolts);
715  }
716  return new PolarEcgData(samples,ecgData.timeStamp);
717  }
718  }).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarEcgData>>() {
719  @Override
720  public Publisher<? extends PolarEcgData> apply(Throwable throwable) throws Exception {
721  return Flowable.error(handleError(throwable));
722  }
723  }).doFinally(new Action() {
724  @Override
725  public void run() throws Exception {
726  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.ECG);
727  }
728  }));
729  } catch (Throwable t){
730  return Flowable.error(t);
731  }
732  }
733 
734  @Override
735  public Flowable<PolarAccelerometerData> startAccStreaming(String identifier,
736  PolarSensorSetting setting) {
737  try {
738  final BleDeviceSession session = sessionPmdClientReady(identifier);
739  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
740  return client.startMeasurement(BlePMDClient.PmdMeasurementType.ACC, setting.map2PmdSettings()).andThen(
741  client.monitorAccNotifications(true).map(new Function<BlePMDClient.AccData, PolarAccelerometerData>() {
742  @Override
743  public PolarAccelerometerData apply(BlePMDClient.AccData accData) throws Exception {
744  List<PolarAccelerometerData.PolarAccelerometerSample> samples = new ArrayList<>();
745  for( BlePMDClient.AccData.AccSample s : accData.accSamples ){
746  samples.add(new PolarAccelerometerData.PolarAccelerometerSample(s.x,s.y,s.z));
747  }
748  return new PolarAccelerometerData(samples,accData.timeStamp);
749  }
750  }).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarAccelerometerData>>() {
751  @Override
752  public Publisher<? extends PolarAccelerometerData> apply(Throwable throwable) throws Exception {
753  return Flowable.error(handleError(throwable));
754  }
755  }).doFinally(new Action() {
756  @Override
757  public void run() throws Exception {
758  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.ACC);
759  }
760  }));
761  } catch (Throwable t){
762  return Flowable.error(t);
763  }
764  }
765 
766  @Override
767  public Flowable<PolarOhrPPGData> startOhrPPGStreaming(String identifier,
768  PolarSensorSetting setting) {
769  try {
770  final BleDeviceSession session = sessionPmdClientReady(identifier);
771  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
772  return client.startMeasurement(BlePMDClient.PmdMeasurementType.PPG, setting.map2PmdSettings()).andThen(
773  client.monitorPpgNotifications(true).map(new Function<BlePMDClient.PpgData, PolarOhrPPGData>() {
774  @Override
775  public PolarOhrPPGData apply(BlePMDClient.PpgData ppgData) throws Exception {
776  List<PolarOhrPPGData.PolarOhrPPGSample> samples = new ArrayList<>();
777  for( BlePMDClient.PpgData.PpgSample s : ppgData.ppgSamples ){
778  samples.add(new PolarOhrPPGData.PolarOhrPPGSample(s.ppg0,s.ppg1,s.ppg2,s.ambient));
779  }
780  return new PolarOhrPPGData(samples,ppgData.timeStamp);
781  }
782  }).doFinally(new Action() {
783  @Override
784  public void run() throws Exception {
785  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.PPG);
786  }
787  })).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarOhrPPGData>>() {
788  @Override
789  public Publisher<? extends PolarOhrPPGData> apply(Throwable throwable) throws Exception {
790  return Flowable.error(handleError(throwable));
791  }
792  });
793  } catch (Throwable t){
794  return Flowable.error(t);
795  }
796  }
797 
798  @Override
799  public Flowable<PolarOhrPPIData> startOhrPPIStreaming(String identifier) {
800  try {
801  final BleDeviceSession session = sessionPmdClientReady(identifier);
802  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
803  return client.startMeasurement(BlePMDClient.PmdMeasurementType.PPI, new BlePMDClient.PmdSetting(new HashMap<BlePMDClient.PmdSetting.PmdSettingType, Integer>())).andThen(
804  client.monitorPpiNotifications(true).map(new Function<BlePMDClient.PpiData, PolarOhrPPIData>() {
805  @Override
806  public PolarOhrPPIData apply(BlePMDClient.PpiData ppiData) throws Exception {
807  List<PolarOhrPPIData.PolarOhrPPISample> samples = new ArrayList<>();
808  for(BlePMDClient.PpiData.PPSample ppSample : ppiData.ppSamples){
809  samples.add(new PolarOhrPPIData.PolarOhrPPISample(ppSample.ppInMs,
810  ppSample.ppErrorEstimate,
811  ppSample.hr,
812  ppSample.blockerBit != 0,
813  ppSample.skinContactStatus != 0,
814  ppSample.skinContactSupported != 0));
815  }
816  return new PolarOhrPPIData(ppiData.timestamp,samples);
817  }
818  }).doFinally(new Action() {
819  @Override
820  public void run() throws Exception {
821  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.PPI);
822  }
823  })).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarOhrPPIData>>() {
824  @Override
825  public Publisher<? extends PolarOhrPPIData> apply(Throwable throwable) throws Exception {
826  return Flowable.error(handleError(throwable));
827  }
828  });
829  } catch (Throwable t){
830  return Flowable.error(t);
831  }
832  }
833 
834  @Override
835  public Flowable<PolarBiozData> startBiozStreaming(final String identifier, PolarSensorSetting setting){
836  try {
837  final BleDeviceSession session = sessionPmdClientReady(identifier);
838  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
839  return client.startMeasurement(BlePMDClient.PmdMeasurementType.BIOZ, setting.map2PmdSettings()).andThen(
840  client.monitorBiozNotifications(true).map(new Function<BlePMDClient.BiozData, PolarBiozData>() {
841  @Override
842  public PolarBiozData apply(BlePMDClient.BiozData biozData) throws Exception {
843  return new PolarBiozData(biozData.timeStamp,biozData.samples);
844  }
845  }).doFinally(new Action() {
846  @Override
847  public void run() throws Exception {
848  stopPmdStreaming(session,client, BlePMDClient.PmdMeasurementType.PPG);
849  }
850  })).onErrorResumeNext(new Function<Throwable, Publisher<? extends PolarBiozData>>() {
851  @Override
852  public Publisher<? extends PolarBiozData> apply(Throwable throwable) throws Exception {
853  return Flowable.error(handleError(throwable));
854  }
855  });
856  } catch (Throwable t){
857  return Flowable.error(t);
858  }
859  }
860 
861  protected BleDeviceSession fetchSession(final String identifier) throws PolarInvalidArgument {
862  if(identifier.matches("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$")){
863  return sessionByAddress(identifier);
864  } else if(identifier.matches("([0-9a-fA-F]){6,8}")) {
865  return sessionByDeviceId(identifier);
866  }
867  throw new PolarInvalidArgument();
868  }
869 
870  protected BleDeviceSession sessionByAddress(final String address) throws PolarInvalidArgument {
871  for ( BleDeviceSession session : listener.deviceSessions() ){
872  if( session.getAddress().equals(address) ){
873  return session;
874  }
875  }
876  return null;
877  }
878 
879  protected BleDeviceSession sessionByDeviceId(final String deviceId) throws PolarInvalidArgument {
880  for ( BleDeviceSession session : listener.deviceSessions() ){
881  if( session.getAdvertisementContent().getPolarDeviceId().equals(deviceId) ){
882  return session;
883  }
884  }
885  return null;
886  }
887 
888  protected BleDeviceSession sessionServiceReady(final String identifier, UUID service) throws Throwable {
889  BleDeviceSession session = fetchSession(identifier);
890  if(session != null){
891  if(session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN) {
892  BleGattBase client = session.fetchClient(service);
893  if (client.isServiceDiscovered()) {
894  return session;
895  }
896  throw new PolarServiceNotAvailable();
897  }
898  throw new PolarDeviceDisconnected();
899  }
900  throw new PolarDeviceNotFound();
901  }
902 
903  public BleDeviceSession sessionPmdClientReady(final String identifier) throws Throwable {
904  BleDeviceSession session = sessionServiceReady(identifier, BlePMDClient.PMD_SERVICE);
905  BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
906  final AtomicInteger pair = client.getNotificationAtomicInteger(BlePMDClient.PMD_CP);
907  final AtomicInteger pairData = client.getNotificationAtomicInteger(BlePMDClient.PMD_DATA);
908  if (pair != null && pairData != null &&
909  pair.get() == BleGattBase.ATT_SUCCESS &&
910  pairData.get() == BleGattBase.ATT_SUCCESS) {
911  return session;
912  }
913  throw new PolarNotificationNotEnabled();
914  }
915 
916  protected BleDeviceSession sessionPsFtpClientReady(final String identifier) throws Throwable {
917  BleDeviceSession session = sessionServiceReady(identifier, BlePsFtpUtils.RFC77_PFTP_SERVICE);
918  BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
919  final AtomicInteger pair = client.getNotificationAtomicInteger(BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC);
920  if (pair != null && pair.get() == BleGattBase.ATT_SUCCESS ) {
921  return session;
922  }
923  throw new PolarNotificationNotEnabled();
924  }
925 
926  @SuppressLint("CheckResult")
927  protected void stopPmdStreaming(BleDeviceSession session, BlePMDClient client, BlePMDClient.PmdMeasurementType type) {
928  if( session.getSessionState() == BleDeviceSession.DeviceSessionState.SESSION_OPEN ){
929  // stop streaming
930  client.stopMeasurement(type).subscribe(
931  new Action() {
932  @Override
933  public void run() throws Exception {
934 
935  }
936  },
937  new Consumer<Throwable>() {
938  @Override
939  public void accept(Throwable throwable) throws Exception {
940  logError("failed to stop pmd stream: " + throwable.getLocalizedMessage());
941  }
942  }
943  );
944  }
945  }
946 
947  @SuppressLint("CheckResult")
948  protected void setupDevice(final BleDeviceSession session){
949  final String deviceId = session.getPolarDeviceId().length() != 0 ? session.getPolarDeviceId() : session.getAddress();
950  session.monitorServicesDiscovered(true).observeOn(scheduler).toFlowable().flatMapIterable(
951  new Function<List<UUID>, Iterable<UUID>>() {
952  @Override
953  public Iterable<UUID> apply(List<UUID> uuids) throws Exception {
954  return uuids;
955  }
956  }
957  ).flatMap(new Function<UUID, Publisher<?>>() {
958  @Override
959  public Publisher<?> apply(UUID uuid) throws Exception {
960  if(session.fetchClient(uuid) != null) {
961  if (uuid.equals(BleHrClient.HR_SERVICE)) {
962  if (callback != null) {
963  callback.hrFeatureReady(deviceId);
964  }
965  final BleHrClient client = (BleHrClient) session.fetchClient(BleHrClient.HR_SERVICE);
966  client.observeHrNotifications(true).observeOn(scheduler).subscribe(
967  new Consumer<BleHrClient.HrNotificationData>() {
968  @Override
969  public void accept(BleHrClient.HrNotificationData hrNotificationData) throws Exception {
970  if (callback != null) {
972  new PolarHrData(hrNotificationData.hrValue,
973  hrNotificationData.rrs,
974  hrNotificationData.sensorContact,
975  hrNotificationData.sensorContactSupported,
976  hrNotificationData.rrPresent));
977  }
978  }
979  },
980  new Consumer<Throwable>() {
981  @Override
982  public void accept(Throwable throwable) throws Exception {
983  logError(throwable.getMessage());
984  }
985  },
986  new Action() {
987  @Override
988  public void run() throws Exception {
989 
990  }
991  }
992  );
993  } else if (uuid.equals(BleBattClient.BATTERY_SERVICE)) {
994  BleBattClient client = (BleBattClient) session.fetchClient(BleBattClient.BATTERY_SERVICE);
995  return client.waitBatteryLevelUpdate(true).observeOn(scheduler).doOnSuccess(new Consumer<Integer>() {
996  @Override
997  public void accept(Integer integer) throws Exception {
998  if (callback != null) {
999  callback.batteryLevelReceived(deviceId, integer);
1000  }
1001  }
1002  }).toFlowable();
1003  } else if (uuid.equals(BlePMDClient.PMD_SERVICE)) {
1004  final BlePMDClient client = (BlePMDClient) session.fetchClient(BlePMDClient.PMD_SERVICE);
1005  return client.waitNotificationEnabled(BlePMDClient.PMD_CP, true).
1006  concatWith(client.waitNotificationEnabled(BlePMDClient.PMD_DATA, true)).andThen(client.readFeature(true).doOnSuccess(new Consumer<BlePMDClient.PmdFeature>() {
1007  @Override
1008  public void accept(BlePMDClient.PmdFeature pmdFeature) {
1009  if (callback != null) {
1010  if (pmdFeature.ecgSupported) {
1011  callback.ecgFeatureReady(deviceId);
1012  }
1013  if (pmdFeature.accSupported) {
1015  }
1016  if (pmdFeature.ppgSupported) {
1017  callback.ppgFeatureReady(deviceId);
1018  }
1019  if (pmdFeature.ppiSupported) {
1020  callback.ppiFeatureReady(deviceId);
1021  }
1022  if (pmdFeature.bioZSupported) {
1023  callback.biozFeatureReady(deviceId);
1024  }
1025  }
1026  }
1027  })).toFlowable();
1028  } else if (uuid.equals(BleDisClient.DIS_SERVICE)) {
1029  BleDisClient client = (BleDisClient) session.fetchClient(BleDisClient.DIS_SERVICE);
1030  return client.observeDisInfo(true).observeOn(scheduler).doOnNext(new Consumer<Pair<UUID, String>>() {
1031  @Override
1032  public void accept(Pair<UUID, String> pair) {
1033  if (callback != null) {
1034  callback.disInformationReceived(deviceId, pair.first , pair.second);
1035  }
1036  }
1037  });
1038  } else if (uuid.equals(BlePsFtpUtils.RFC77_PFTP_SERVICE)) {
1039  BlePsFtpClient client = (BlePsFtpClient) session.fetchClient(BlePsFtpUtils.RFC77_PFTP_SERVICE);
1040  return client.waitPsFtpClientReady(true).observeOn(scheduler).doOnComplete(new Action() {
1041  @Override
1042  public void run() throws Exception {
1043  if (callback != null &&
1044  (session.getPolarDeviceType().equals("OH1") || session.getPolarDeviceType().equals("H10"))) {
1045  callback.polarFtpFeatureReady(deviceId);
1046  }
1047  }
1048  }).toFlowable();
1049  }
1050  }
1051  return Flowable.empty();
1052  }
1053  }).subscribe(
1054  new Consumer<Object>() {
1055  @Override
1056  public void accept(Object o) throws Exception {
1057 
1058  }
1059  },
1060  new Consumer<Throwable>() {
1061  @Override
1062  public void accept(Throwable throwable) throws Exception {
1063  logError(throwable.getMessage());
1064  }
1065  },
1066  new Action() {
1067  @Override
1068  public void run() throws Exception {
1069  log("complete");
1070  }
1071  });
1072  }
1073 
1074  protected Exception handleError(Throwable throwable) {
1075  if( throwable instanceof BleDisconnected ){
1076  return new PolarDeviceDisconnected();
1077  } else {
1078  return new Exception("Unknown Error: " + throwable.getLocalizedMessage());
1079  }
1080  }
1081 
1082  interface FetchRecursiveCondition {
1083  boolean include(String entry);
1084  }
1085 
1086  protected Flowable<String> fetchRecursively(final BlePsFtpClient client, final String path, final FetchRecursiveCondition condition) {
1087  protocol.PftpRequest.PbPFtpOperation.Builder builder = protocol.PftpRequest.PbPFtpOperation.newBuilder();
1088  builder.setCommand(PftpRequest.PbPFtpOperation.Command.GET);
1089  builder.setPath(path);
1090  return client.request(builder.build().toByteArray()).toFlowable().flatMap(new Function<ByteArrayOutputStream, Publisher<String>>() {
1091  @Override
1092  public Publisher<String> apply(ByteArrayOutputStream byteArrayOutputStream) throws Exception {
1093  PftpResponse.PbPFtpDirectory dir = PftpResponse.PbPFtpDirectory.parseFrom(byteArrayOutputStream.toByteArray());
1094  Set<String> entrys = new HashSet<>();
1095  for( int i=0; i < dir.getEntriesCount(); ++i ){
1096  PftpResponse.PbPFtpEntry entry = dir.getEntries(i);
1097  if( condition.include(entry.getName()) ){
1098  BleUtils.validate(entrys.add(path + entry.getName()),"duplicate entry");
1099  }
1100  }
1101  if(entrys.size()!=0) {
1102  return Flowable.fromIterable(entrys).flatMap(new Function<String, Publisher<String>>() {
1103  @Override
1104  public Publisher<String> apply(String s) {
1105  if (s.endsWith("/")) {
1106  return fetchRecursively(client, s, condition);
1107  } else {
1108  return Flowable.just(s);
1109  }
1110  }
1111  });
1112  }
1113  return Flowable.empty();
1114  }
1115  });
1116  }
1117 
1118  protected void log(final String message) {
1119  if(logger != null){
1120  logger.message("" + message);
1121  }
1122  }
1123 
1124  protected void logError(final String message) {
1125  if(logger != null){
1126  logger.message("Error: "+message);
1127  }
1128  }
1129 }
+
void log(final String message)
+
void blePowerStateChanged(final boolean powered)
static final int ANDROID_VERSION_O
Single< PolarExerciseData > fetchExercise(String identifier, PolarExerciseEntry entry)
@@ -78,49 +79,46 @@
void setAutomaticReconnection(boolean disable)
Single< PolarSensorSetting > requestAccSettings(String identifier)
PolarBleApiCallback callback
-
Exception handleError(Throwable throwable)
- +
Exception handleError(Throwable throwable)
-
void accelerometerFeatureReady(@NonNull final String identifier)
+
void accelerometerFeatureReady(@NonNull final String identifier)
Flowable< PolarOhrPPIData > startOhrPPIStreaming(String identifier)
-
void deviceConnecting(@NonNull final PolarDeviceInfo polarDeviceInfo)
-
void hrFeatureReady(@NonNull final String identifier)
Flowable< PolarHrBroadcastData > startListenForPolarHrBroadcasts(final Set< String > deviceIds)
Flowable< PolarDeviceInfo > searchForDevice()
-
void ecgFeatureReady(@NonNull final String identifier)
+
void polarFtpFeatureReady(@NonNull final String identifier)
+
Single< PolarSensorSetting > querySettings(final String identifier, final BlePMDClient.PmdMeasurementType type)
static final int FEATURE_POLAR_SENSOR_STREAMING
+
void deviceDisconnected(@NonNull final PolarDeviceInfo polarDeviceInfo)
+
void disInformationReceived(@NonNull final String identifier, @NonNull UUID uuid, @NonNull final String value)
Map< String, Disposable > connectSubscriptions
-
void ppgFeatureReady(@NonNull final String identifier)
Single< PolarSensorSetting > requestBiozSettings(final String identifier)
Single< PolarSensorSetting > requestEcgSettings(String identifier)
-
BleDeviceSession sessionServiceReady(final String identifier, UUID service)
+
BleDeviceSession sessionServiceReady(final String identifier, UUID service)
+
void ecgFeatureReady(@NonNull final String identifier)
Single< Pair< Boolean, String > > requestRecordingStatus(String identifier)
static final int FEATURE_BATTERY_INFO
-
void disInformationReceived(@NonNull final String identifier, @NonNull UUID uuid, @NonNull final String value)
BleDeviceSession fetchSession(final String identifier)
boolean isFeatureReady(final String deviceId, int feature)
-
BleDeviceSession sessionByAddress(final String address)
+
BleDeviceSession sessionByAddress(final String address)
-
void deviceConnected(@NonNull final PolarDeviceInfo polarDeviceInfo)
-
Flowable< PolarAccelerometerData > startAccStreaming(String identifier, PolarSensorSetting setting)
BDBleApiImpl(final Context context, int features)
@@ -128,16 +126,17 @@ -
BleDeviceSession sessionPmdClientReady(final String identifier)
+
BleDeviceSession sessionPmdClientReady(final String identifier)
+
void deviceConnecting(@NonNull final PolarDeviceInfo polarDeviceInfo)
-
Flowable< String > fetchRecursively(final BlePsFtpClient client, final String path, final FetchRecursiveCondition condition)
+
Flowable< String > fetchRecursively(final BlePsFtpClient client, final String path, final FetchRecursiveCondition condition)
Completable stopRecording(String identifier)
-
void ppiFeatureReady(@NonNull final String identifier)
-
void batteryLevelReceived(@NonNull final String identifier, final int level)
+
void ppiFeatureReady(@NonNull final String identifier)
static final int FEATURE_DEVICE_INFO
-
void setupDevice(final BleDeviceSession session)
+
void setupDevice(final BleDeviceSession session)
+
void batteryLevelReceived(@NonNull final String identifier, final int level)
void connectToDevice(final String identifier)
Flowable< PolarBiozData > startBiozStreaming(final String identifier, PolarSensorSetting setting)
@@ -145,24 +144,23 @@
Completable autoConnectToDevice(final int rssiLimit, final String service, final String polarDeviceType)
-
BleDeviceSession sessionByDeviceId(final String deviceId)
-
void hrNotificationReceived(@NonNull final String identifier, @NonNull final PolarHrData data)
+
BleDeviceSession sessionByDeviceId(final String deviceId)
void setPolarFilter(boolean enable)
-
void blePowerStateChanged(final boolean powered)
static final int FEATURE_POLAR_FILE_TRANSFER
Completable removeExercise(String identifier, PolarExerciseEntry entry)
-
void polarFtpFeatureReady(@NonNull final String identifier)
-
void deviceDisconnected(@NonNull final PolarDeviceInfo polarDeviceInfo)
+
void ppgFeatureReady(@NonNull final String identifier)
+
void biozFeatureReady(@NonNull final String identifier)
-
void stopPmdStreaming(BleDeviceSession session, BlePMDClient client, BlePMDClient.PmdMeasurementType type)
+
void stopPmdStreaming(BleDeviceSession session, BlePMDClient client, BlePMDClient.PmdMeasurementType type)
void setApiLogger(@Nullable PolarBleApiLogger logger)
Flowable< PolarOhrPPGData > startOhrPPGStreaming(String identifier, PolarSensorSetting setting)
Flowable< PolarEcgData > startEcgStreaming(String identifier, PolarSensorSetting setting)
-
void biozFeatureReady(@NonNull final String identifier)
+
void hrNotificationReceived(@NonNull final String identifier, @NonNull final PolarHrData data)
+
void hrFeatureReady(@NonNull final String identifier)
@@ -170,9 +168,10 @@
void setApiCallback(PolarBleApiCallback callback)
Completable setLocalTime(String identifier, Calendar cal)
+
void deviceConnected(@NonNull final PolarDeviceInfo polarDeviceInfo)
-
BleDeviceSession sessionPsFtpClientReady(final String identifier)
-
void logError(final String message)
+
BleDeviceSession sessionPsFtpClientReady(final String identifier)
+
void logError(final String message)
diff --git a/polar-sdk-android/docs/html/PolarBleApiCallback_8java.html b/polar-sdk-android/docs/html/PolarBleApiCallback_8java.html index d758626a..71d0859f 100644 --- a/polar-sdk-android/docs/html/PolarBleApiCallback_8java.html +++ b/polar-sdk-android/docs/html/PolarBleApiCallback_8java.html @@ -74,7 +74,7 @@ - +

Data Structures

interface  polar.com.sdk.api.PolarBleApiCallback
class  polar.com.sdk.api.PolarBleApiCallback
 
- + diff --git a/polar-sdk-android/docs/html/classes.html b/polar-sdk-android/docs/html/classes.html index 0cc5a7dc..139fbf3d 100644 --- a/polar-sdk-android/docs/html/classes.html +++ b/polar-sdk-android/docs/html/classes.html @@ -71,7 +71,7 @@ - + diff --git a/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi.html b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi.html index 8c2a0449..127f9980 100644 --- a/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi.html +++ b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi.html @@ -105,7 +105,7 @@ - + @@ -848,7 +848,7 @@

abstract void polar.com.sdk.api.PolarBleApi.setApiCallback

- + @@ -865,7 +865,7 @@

See also
polar.com.sdk.api.PolarBleApiCallback
+
See also
polar.com.sdk.api.PolarBleApiCallback
@@ -1281,7 +1281,7 @@

-

Start OHR (Optical heart rate) PPI (Pulse to Pulse interval) stream. Notice that there is a delay before PPI data stream starts Requires feature PolarBleApi::FEATURE_POLAR_SENSOR_STREAMING.

Parameters
+

Start OHR (Optical heart rate) PPI (Pulse to Pulse interval) stream. Notice that there is a delay before PPI data stream starts Requires feature PolarBleApi::FEATURE_POLAR_SENSOR_STREAMING. When using OH1 timestamp produced by PPI stream is 0.

Parameters

diff --git a/polar-sdk-android/docs/html/PolarBleApiCallback_8java_source.html b/polar-sdk-android/docs/html/PolarBleApiCallback_8java_source.html index 440a0008..97153d7a 100644 --- a/polar-sdk-android/docs/html/PolarBleApiCallback_8java_source.html +++ b/polar-sdk-android/docs/html/PolarBleApiCallback_8java_source.html @@ -66,27 +66,27 @@
PolarBleApiCallback.java
-Go to the documentation of this file.
1 // Copyright © 2019 Polar Electro Oy. All rights reserved.
2 package polar.com.sdk.api;
3 
4 import java.util.UUID;
5 
6 import io.reactivex.annotations.NonNull;
9 
13 public interface PolarBleApiCallback {
14 
18  void blePowerStateChanged(final boolean powered);
19 
24  void deviceConnected(@NonNull final PolarDeviceInfo polarDeviceInfo);
25 
30  void deviceConnecting(@NonNull final PolarDeviceInfo polarDeviceInfo);
31 
37  void deviceDisconnected(@NonNull final PolarDeviceInfo polarDeviceInfo);
38 
44  void ecgFeatureReady(@NonNull final String identifier);
45 
51  void accelerometerFeatureReady(@NonNull final String identifier);
52 
58  void ppgFeatureReady(@NonNull final String identifier);
59 
65  void ppiFeatureReady(@NonNull final String identifier);
66 
71  void biozFeatureReady(@NonNull final String identifier);
72 
77  void hrFeatureReady(@NonNull final String identifier);
78 
86  void disInformationReceived(@NonNull final String identifier, @NonNull UUID uuid, @NonNull final String value);
87 
94  void batteryLevelReceived(@NonNull final String identifier, final int level);
95 
101  void hrNotificationReceived(@NonNull final String identifier,@NonNull final PolarHrData data);
102 
108  void polarFtpFeatureReady(@NonNull final String identifier);
109 }
+Go to the documentation of this file.
1 // Copyright © 2019 Polar Electro Oy. All rights reserved.
2 package polar.com.sdk.api;
3 
4 import java.util.UUID;
5 
6 import io.reactivex.annotations.NonNull;
9 
13 public abstract class PolarBleApiCallback {
14 
18  public void blePowerStateChanged(final boolean powered) {}
19 
24  public void deviceConnected(@NonNull final PolarDeviceInfo polarDeviceInfo) {}
25 
30  public void deviceConnecting(@NonNull final PolarDeviceInfo polarDeviceInfo) {}
31 
37  public void deviceDisconnected(@NonNull final PolarDeviceInfo polarDeviceInfo) {}
38 
44  public void ecgFeatureReady(@NonNull final String identifier) {}
45 
51  public void accelerometerFeatureReady(@NonNull final String identifier) {}
52 
58  public void ppgFeatureReady(@NonNull final String identifier) {}
59 
65  public void ppiFeatureReady(@NonNull final String identifier) {}
66 
71  public void biozFeatureReady(@NonNull final String identifier) {}
72 
77  public void hrFeatureReady(@NonNull final String identifier) {}
78 
86  public void disInformationReceived(@NonNull final String identifier, @NonNull UUID uuid, @NonNull final String value) {}
87 
94  public void batteryLevelReceived(@NonNull final String identifier, final int level) {}
95 
102  public void hrNotificationReceived(@NonNull final String identifier,@NonNull final PolarHrData data) {}
103 
109  public void polarFtpFeatureReady(@NonNull final String identifier) {}
110 }
void blePowerStateChanged(final boolean powered)
-
void accelerometerFeatureReady(@NonNull final String identifier)
-
void deviceConnecting(@NonNull final PolarDeviceInfo polarDeviceInfo)
-
void hrFeatureReady(@NonNull final String identifier)
-
void ecgFeatureReady(@NonNull final String identifier)
+
void accelerometerFeatureReady(@NonNull final String identifier)
+
void polarFtpFeatureReady(@NonNull final String identifier)
+ +
void deviceDisconnected(@NonNull final PolarDeviceInfo polarDeviceInfo)
+
void disInformationReceived(@NonNull final String identifier, @NonNull UUID uuid, @NonNull final String value)
-
void ppgFeatureReady(@NonNull final String identifier)
+
void ecgFeatureReady(@NonNull final String identifier)
-
void disInformationReceived(@NonNull final String identifier, @NonNull UUID uuid, @NonNull final String value)
-
void deviceConnected(@NonNull final PolarDeviceInfo polarDeviceInfo)
-
void ppiFeatureReady(@NonNull final String identifier)
+
void deviceConnecting(@NonNull final PolarDeviceInfo polarDeviceInfo)
-
void batteryLevelReceived(@NonNull final String identifier, final int level)
-
void hrNotificationReceived(@NonNull final String identifier, @NonNull final PolarHrData data)
-
void blePowerStateChanged(final boolean powered)
-
void polarFtpFeatureReady(@NonNull final String identifier)
-
void deviceDisconnected(@NonNull final PolarDeviceInfo polarDeviceInfo)
-
void biozFeatureReady(@NonNull final String identifier)
+
void ppiFeatureReady(@NonNull final String identifier)
+
void batteryLevelReceived(@NonNull final String identifier, final int level)
+
void ppgFeatureReady(@NonNull final String identifier)
+
void biozFeatureReady(@NonNull final String identifier)
+
void hrNotificationReceived(@NonNull final String identifier, @NonNull final PolarHrData data)
+
void hrFeatureReady(@NonNull final String identifier)
+
void deviceConnected(@NonNull final PolarDeviceInfo polarDeviceInfo)
diff --git a/polar-sdk-android/docs/html/PolarBleApi_8java_source.html b/polar-sdk-android/docs/html/PolarBleApi_8java_source.html index fb3c9082..91673c1e 100644 --- a/polar-sdk-android/docs/html/PolarBleApi_8java_source.html +++ b/polar-sdk-android/docs/html/PolarBleApi_8java_source.html @@ -66,10 +66,9 @@
PolarBleApi.java
-Go to the documentation of this file.
1 // Copyright © 2019 Polar Electro Oy. All rights reserved.
2 package polar.com.sdk.api;
3 
4 import android.support.annotation.Nullable;
5 import android.support.annotation.Size;
6 import android.util.Pair;
7 
8 import java.util.Calendar;
9 import java.util.Date;
10 import java.util.Set;
11 import java.util.concurrent.TimeUnit;
12 
13 import io.reactivex.Completable;
14 import io.reactivex.Flowable;
15 import io.reactivex.Single;
16 import io.reactivex.annotations.NonNull;
28 
32 public abstract class PolarBleApi {
33 
37  public interface PolarBleApiLogger {
42  void message(final String str);
43  }
44 
48  public enum RecordingInterval {
52  private int value;
53 
54  RecordingInterval(int value) {
55  this.value = value;
56  }
57 
58  public int getValue() {
59  return value;
60  }
61  }
62 
66  public enum SampleType {
67  HR,
68  RR;
69  }
70 
74  public static final int FEATURE_HR = 1;
78  public static final int FEATURE_DEVICE_INFO = 2;
82  public static final int FEATURE_BATTERY_INFO = 4;
86  public static final int FEATURE_POLAR_SENSOR_STREAMING = 8;
90  public static final int FEATURE_POLAR_FILE_TRANSFER = 16;
94  public static final int ALL_FEATURES = 0xff;
95 
96  protected int features;
97 
102  protected PolarBleApi(final int features) {
103  this.features = features;
104  }
105 
109  public abstract void shutDown();
110 
114  public abstract void cleanup();
115 
120  public abstract void setPolarFilter(boolean enable);
121 
128  public abstract boolean isFeatureReady(@NonNull final String deviceId, final int feature);
129 
133  public abstract void backgroundEntered();
134 
138  public abstract void foregroundEntered();
139 
143  public abstract void setApiCallback(@Nullable PolarBleApiCallback callback);
144 
148  public abstract void setApiLogger(@Nullable PolarBleApiLogger logger);
149 
154  public abstract void setAutomaticReconnection(boolean enable);
155 
163  public abstract Completable setLocalTime(@NonNull final String identifier, @NonNull Calendar calendar);
164 
170  public abstract Single<PolarSensorSetting> requestAccSettings(@NonNull final String identifier);
171 
177  public abstract Single<PolarSensorSetting> requestEcgSettings(@NonNull final String identifier);
178 
184  public abstract Single<PolarSensorSetting> requestPpgSettings(@NonNull final String identifier);
185 
186  public abstract Single<PolarSensorSetting> requestBiozSettings(@NonNull final String identifier);
187 
199  public abstract Completable autoConnectToDevice(int rssiLimit, @Nullable String service, int timeout,@NonNull TimeUnit unit,@Nullable final String polarDeviceType);
200  public abstract Completable autoConnectToDevice(int rssiLimit, @Nullable String service, final String polarDeviceType);
201 
207  public abstract void connectToDevice(@NonNull final String identifier) throws PolarInvalidArgument;
208 
214  public abstract void disconnectFromDevice(@NonNull final String identifier) throws PolarInvalidArgument;
215 
224  public abstract Completable startRecording(@NonNull final String identifier,
225  @NonNull @Size(min = 1, max = 64) final String exerciseId,
226  @NonNull RecordingInterval interval,
227  @NonNull SampleType type);
228 
234  public abstract Completable stopRecording(@NonNull final String identifier);
235 
241  public abstract Single<Pair<Boolean,String>> requestRecordingStatus(@NonNull final String identifier);
242 
248  public abstract Flowable<PolarExerciseEntry> listExercises(@NonNull final String identifier);
249 
256  public abstract Single<PolarExerciseData> fetchExercise(@NonNull final String identifier, @NonNull final PolarExerciseEntry entry);
257 
264  public abstract Completable removeExercise(@NonNull final String identifier, @NonNull final PolarExerciseEntry entry);
265 
274  public abstract Flowable<PolarDeviceInfo> searchForDevice();
275 
285  public abstract Flowable<PolarHrBroadcastData> startListenForPolarHrBroadcasts(@Nullable final Set<String> deviceIds);
286 
299  public abstract Flowable<PolarEcgData> startEcgStreaming(@NonNull final String identifier,
300  @NonNull PolarSensorSetting sensorSetting);
301 
314  public abstract Flowable<PolarAccelerometerData> startAccStreaming(@NonNull final String identifier,
315  @NonNull PolarSensorSetting sensorSetting);
316 
329  public abstract Flowable<PolarOhrPPGData> startOhrPPGStreaming(@NonNull final String identifier,
330  @NonNull PolarSensorSetting sensorSetting);
331 
332  public abstract Flowable<PolarBiozData> startBiozStreaming(@NonNull final String identifier,
333  @NonNull PolarSensorSetting sensorSetting);
334 
345  public abstract Flowable<PolarOhrPPIData> startOhrPPIStreaming(@NonNull final String identifier);
346 }
+Go to the documentation of this file.
1 // Copyright © 2019 Polar Electro Oy. All rights reserved.
2 package polar.com.sdk.api;
3 
4 import android.support.annotation.Nullable;
5 import android.support.annotation.Size;
6 import android.util.Pair;
7 
8 import java.util.Calendar;
9 import java.util.Date;
10 import java.util.Set;
11 import java.util.concurrent.TimeUnit;
12 
13 import io.reactivex.Completable;
14 import io.reactivex.Flowable;
15 import io.reactivex.Single;
16 import io.reactivex.annotations.NonNull;
28 
32 public abstract class PolarBleApi {
33 
37  public interface PolarBleApiLogger {
42  void message(final String str);
43  }
44 
48  public enum RecordingInterval {
52  private int value;
53 
54  RecordingInterval(int value) {
55  this.value = value;
56  }
57 
58  public int getValue() {
59  return value;
60  }
61  }
62 
66  public enum SampleType {
67  HR,
68  RR;
69  }
70 
74  public static final int FEATURE_HR = 1;
78  public static final int FEATURE_DEVICE_INFO = 2;
82  public static final int FEATURE_BATTERY_INFO = 4;
86  public static final int FEATURE_POLAR_SENSOR_STREAMING = 8;
90  public static final int FEATURE_POLAR_FILE_TRANSFER = 16;
94  public static final int ALL_FEATURES = 0xff;
95 
96  protected int features;
97 
102  protected PolarBleApi(final int features) {
103  this.features = features;
104  }
105 
109  public abstract void shutDown();
110 
114  public abstract void cleanup();
115 
120  public abstract void setPolarFilter(boolean enable);
121 
128  public abstract boolean isFeatureReady(@NonNull final String deviceId, final int feature);
129 
133  public abstract void backgroundEntered();
134 
138  public abstract void foregroundEntered();
139 
143  public abstract void setApiCallback(@Nullable PolarBleApiCallback callback);
144 
148  public abstract void setApiLogger(@Nullable PolarBleApiLogger logger);
149 
154  public abstract void setAutomaticReconnection(boolean enable);
155 
163  public abstract Completable setLocalTime(@NonNull final String identifier, @NonNull Calendar calendar);
164 
170  public abstract Single<PolarSensorSetting> requestAccSettings(@NonNull final String identifier);
171 
177  public abstract Single<PolarSensorSetting> requestEcgSettings(@NonNull final String identifier);
178 
184  public abstract Single<PolarSensorSetting> requestPpgSettings(@NonNull final String identifier);
185 
186  public abstract Single<PolarSensorSetting> requestBiozSettings(@NonNull final String identifier);
187 
199  public abstract Completable autoConnectToDevice(int rssiLimit, @Nullable String service, int timeout,@NonNull TimeUnit unit,@Nullable final String polarDeviceType);
200  public abstract Completable autoConnectToDevice(int rssiLimit, @Nullable String service, final String polarDeviceType);
201 
207  public abstract void connectToDevice(@NonNull final String identifier) throws PolarInvalidArgument;
208 
214  public abstract void disconnectFromDevice(@NonNull final String identifier) throws PolarInvalidArgument;
215 
224  public abstract Completable startRecording(@NonNull final String identifier,
225  @NonNull @Size(min = 1, max = 64) final String exerciseId,
226  @NonNull RecordingInterval interval,
227  @NonNull SampleType type);
228 
234  public abstract Completable stopRecording(@NonNull final String identifier);
235 
241  public abstract Single<Pair<Boolean,String>> requestRecordingStatus(@NonNull final String identifier);
242 
248  public abstract Flowable<PolarExerciseEntry> listExercises(@NonNull final String identifier);
249 
256  public abstract Single<PolarExerciseData> fetchExercise(@NonNull final String identifier, @NonNull final PolarExerciseEntry entry);
257 
264  public abstract Completable removeExercise(@NonNull final String identifier, @NonNull final PolarExerciseEntry entry);
265 
274  public abstract Flowable<PolarDeviceInfo> searchForDevice();
275 
285  public abstract Flowable<PolarHrBroadcastData> startListenForPolarHrBroadcasts(@Nullable final Set<String> deviceIds);
286 
299  public abstract Flowable<PolarEcgData> startEcgStreaming(@NonNull final String identifier,
300  @NonNull PolarSensorSetting sensorSetting);
301 
314  public abstract Flowable<PolarAccelerometerData> startAccStreaming(@NonNull final String identifier,
315  @NonNull PolarSensorSetting sensorSetting);
316 
329  public abstract Flowable<PolarOhrPPGData> startOhrPPGStreaming(@NonNull final String identifier,
330  @NonNull PolarSensorSetting sensorSetting);
331 
332  public abstract Flowable<PolarBiozData> startBiozStreaming(@NonNull final String identifier,
333  @NonNull PolarSensorSetting sensorSetting);
334 
345  public abstract Flowable<PolarOhrPPIData> startOhrPPIStreaming(@NonNull final String identifier);
346 }
abstract Single< PolarSensorSetting > requestEcgSettings(@NonNull final String identifier)
abstract Flowable< PolarOhrPPIData > startOhrPPIStreaming(@NonNull final String identifier)
-
abstract Flowable< PolarAccelerometerData > startAccStreaming(@NonNull final String identifier, @NonNull PolarSensorSetting sensorSetting)
@@ -79,6 +78,7 @@
abstract Completable stopRecording(@NonNull final String identifier)
+
abstract Single< PolarSensorSetting > requestAccSettings(@NonNull final String identifier)
abstract void setAutomaticReconnection(boolean enable)
abstract void connectToDevice(@NonNull final String identifier)
diff --git a/polar-sdk-android/docs/html/annotated.html b/polar-sdk-android/docs/html/annotated.html index 849b2a73..e3873b17 100644 --- a/polar-sdk-android/docs/html/annotated.html +++ b/polar-sdk-android/docs/html/annotated.html @@ -97,7 +97,7 @@

 CPolarBleApiLogger
 CRecordingInterval
 CSampleType
 CPolarBleApiCallback
 CPolarBleApiCallback
 CPolarBleApiDefaultImpl
 Nimpl
 CBDBleApiImpl
  b  
PolarBleApi (polar.com.sdk.api)   PolarDeviceNotFound (polar.com.sdk.api.errors)   PolarNotificationNotEnabled (polar.com.sdk.api.errors)   
  s  
PolarBleApiCallback (polar.com.sdk.api)   PolarEcgData (polar.com.sdk.api.model)   PolarOhrPPGData (polar.com.sdk.api.model)   
PolarBleApiCallback (polar.com.sdk.api)   PolarEcgData (polar.com.sdk.api.model)   PolarOhrPPGData (polar.com.sdk.api.model)   
BDBleApiImpl (polar.com.sdk.impl)   PolarBleApiDefaultImpl (polar.com.sdk.api)   PolarExerciseData (polar.com.sdk.api.model)   PolarOhrPPIData (polar.com.sdk.api.model)   PolarBleApi.SampleType (polar.com.sdk.api)   
  p  
PolarBleApi.PolarBleApiLogger (polar.com.sdk.api)   PolarExerciseEntry (polar.com.sdk.api.model)   PolarOperationNotSupported (polar.com.sdk.api.errors)   PolarSensorSetting.SettingType (polar.com.sdk.api.model)   
 
abstract void foregroundEntered ()
 
abstract void setApiCallback (@Nullable PolarBleApiCallback callback)
abstract void setApiCallback (@Nullable PolarBleApiCallback callback)
 
abstract void setApiLogger (@Nullable PolarBleApiLogger logger)
 
(@Nullable PolarBleApiCallback @Nullable PolarBleApiCallback  callback)
identifierPolar device id found printed on the sensor/device or bt address
diff --git a/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html new file mode 100644 index 00000000..200d1642 --- /dev/null +++ b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html @@ -0,0 +1,640 @@ + + + + + + + +SDK for Polar sensors: polar.com.sdk.api.PolarBleApiCallback Class Reference + + + + + + + + + + + +
+
+ + + + + + +
+
SDK for Polar sensors +
+
+
+ + + + + + +
+
+ +
+
+
+ +
+
+ +
+
polar.com.sdk.api.PolarBleApiCallback Class Referenceabstract
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+Public Member Functions

void blePowerStateChanged (final boolean powered)
 
void deviceConnected (@NonNull final PolarDeviceInfo polarDeviceInfo)
 
void deviceConnecting (@NonNull final PolarDeviceInfo polarDeviceInfo)
 
void deviceDisconnected (@NonNull final PolarDeviceInfo polarDeviceInfo)
 
void ecgFeatureReady (@NonNull final String identifier)
 
void accelerometerFeatureReady (@NonNull final String identifier)
 
void ppgFeatureReady (@NonNull final String identifier)
 
void ppiFeatureReady (@NonNull final String identifier)
 
void biozFeatureReady (@NonNull final String identifier)
 
void hrFeatureReady (@NonNull final String identifier)
 
void disInformationReceived (@NonNull final String identifier, @NonNull UUID uuid, @NonNull final String value)
 
void batteryLevelReceived (@NonNull final String identifier, final int level)
 
void hrNotificationReceived (@NonNull final String identifier, @NonNull final PolarHrData data)
 
void polarFtpFeatureReady (@NonNull final String identifier)
 
+

Detailed Description

+

Contains the callbacks of the API.

+ +

Definition at line 13 of file PolarBleApiCallback.java.

+

Member Function Documentation

+ +

◆ accelerometerFeatureReady()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.accelerometerFeatureReady (@NonNull final String identifier)
+
+inline
+
+

Polar device's ACC feature is ready. Application may start ACC stream now if desired. requires feature PolarBleApi::FEATURE_POLAR_SENSOR_STREAMING

Parameters
+ + +
identifierPolar device id
+
+
+ +

Definition at line 51 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ batteryLevelReceived()

+ +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.batteryLevelReceived (@NonNull final String identifier,
final int level 
)
+
+inline
+
+

Battery level received requires feature PolarBleApi::FEATURE_BATTERY_INFO

Parameters
+ + + +
identifierPolar device id or bt address
levelbattery level (value between 0-100%)
+
+
+ +

Definition at line 94 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ biozFeatureReady()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.biozFeatureReady (@NonNull final String identifier)
+
+inline
+
+

Polar device's bioz feature is ready.

Parameters
+ + +
identifierPolar device id
+
+
+ +

Definition at line 71 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ blePowerStateChanged()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.blePowerStateChanged (final boolean powered)
+
+inline
+
+
Parameters
+ + +
poweredtrue = Bluetooth power on, false = Bluetooth power off
+
+
+ +

Definition at line 18 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ deviceConnected()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.deviceConnected (@NonNull final PolarDeviceInfo polarDeviceInfo)
+
+inline
+
+

Device is now connected

Parameters
+ + +
polarDeviceInfoPolar device information
+
+
+ +

Definition at line 24 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ deviceConnecting()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.deviceConnecting (@NonNull final PolarDeviceInfo polarDeviceInfo)
+
+inline
+
+

Connecting to device

Parameters
+ + +
polarDeviceInfoPolar device information
+
+
+ +

Definition at line 30 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ deviceDisconnected()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.deviceDisconnected (@NonNull final PolarDeviceInfo polarDeviceInfo)
+
+inline
+
+

Device is now disconnected, no further action is needed from the application if polar.com.sdk.api.PolarBleApi::disconnectFromPolarDevice is not called. Device will be automatically reconnected

Parameters
+ + +
polarDeviceInfoPolar device information
+
+
+ +

Definition at line 37 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ disInformationReceived()

+ +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.disInformationReceived (@NonNull final String identifier,
@NonNull UUID uuid,
@NonNull final String value 
)
+
+inline
+
+

DIS information received requires feature PolarBleApi::FEATURE_DEVICE_INFO

Parameters
+ + + + +
identifierPolar device id or bt address
uuiduuid of dis value
valuedis value for uuid
+
+
+ +

Definition at line 86 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ ecgFeatureReady()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.ecgFeatureReady (@NonNull final String identifier)
+
+inline
+
+

Polar device's ECG feature is ready. Application may start ECG stream now if desired. requires feature PolarBleApi::FEATURE_POLAR_SENSOR_STREAMING

Parameters
+ + +
identifierPolar device id
+
+
+ +

Definition at line 44 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ hrFeatureReady()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.hrFeatureReady (@NonNull final String identifier)
+
+inline
+
+

Polar device HR client is now ready and HR transmission is starting in a moment.

Parameters
+ + +
identifierPolar device id or bt address
+
+
+ +

Definition at line 77 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ hrNotificationReceived()

+ +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.hrNotificationReceived (@NonNull final String identifier,
@NonNull final PolarHrData data 
)
+
+inline
+
+

HR notification data received from device. Notice when using OH1 and PPI measurement is started hr received from this callback is 0.

Parameters
+ + + +
identifierPolar device id or bt address
data
+
+
+
See also
polar.com.sdk.api.model.PolarHrData.java
+ +

Definition at line 102 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ polarFtpFeatureReady()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.polarFtpFeatureReady (@NonNull final String identifier)
+
+inline
+
+

File transfer ready requires feature PolarBleApi::FEATURE_POLAR_FILE_TRANSFER

Parameters
+ + +
identifierPolar device id
+
+
+ +

Definition at line 109 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ ppgFeatureReady()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.ppgFeatureReady (@NonNull final String identifier)
+
+inline
+
+

Polar device's PPG feature is ready. Application may start PPG stream now if desired. requires feature PolarBleApi::FEATURE_POLAR_SENSOR_STREAMING

Parameters
+ + +
identifierPolar device id
+
+
+ +

Definition at line 58 of file PolarBleApiCallback.java.

+ +
+
+ +

◆ ppiFeatureReady()

+ +
+
+ + + + + +
+ + + + + + + + +
void polar.com.sdk.api.PolarBleApiCallback.ppiFeatureReady (@NonNull final String identifier)
+
+inline
+
+

Polar device's PPI feature is ready. Application may start PPI stream now if desired. requires feature PolarBleApi::FEATURE_POLAR_SENSOR_STREAMING

Parameters
+ + +
identifierPolar device id
+
+
+ +

Definition at line 65 of file PolarBleApiCallback.java.

+ +
+
+
The documentation for this class was generated from the following file: +
+
+ + + + diff --git a/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.js b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.js new file mode 100644 index 00000000..035c222d --- /dev/null +++ b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.js @@ -0,0 +1,17 @@ +var classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback = +[ + [ "accelerometerFeatureReady", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a2994bd2e8b3d80b4e4de9ad0bb0920e8", null ], + [ "batteryLevelReceived", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ad98839ae9719804e7f93db93c03a3502", null ], + [ "biozFeatureReady", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#afec6d56715ba82b46129812be452f1ba", null ], + [ "blePowerStateChanged", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a2e4e8f71a75eebf87be49751fcf4b4e8", null ], + [ "deviceConnected", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ae98b2e7a18f12255b955693e1afc7bf2", null ], + [ "deviceConnecting", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ab6b0fa75da852019c7b74820e00f2cb9", null ], + [ "deviceDisconnected", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a8d2b40c2a8b2b14d98778f7542e337b8", null ], + [ "disInformationReceived", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ae9028fa8d2b8a017db6c01d061a69c8e", null ], + [ "ecgFeatureReady", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a3c2c3ff7eb76a8cba67057b60122b035", null ], + [ "hrFeatureReady", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a60b5030d4ee50f7cf9d50bcbe2229808", null ], + [ "hrNotificationReceived", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#aa9534a4ec9f2269d19a112f54d2ec0e0", null ], + [ "polarFtpFeatureReady", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ad2055f89f0e3a0d1dfce7a444b507698", null ], + [ "ppgFeatureReady", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a3541b2cec92744126a84ec207f3e1211", null ], + [ "ppiFeatureReady", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#af5ab3ec3f17b4f32085e9e085b0f38c3", null ] +]; \ No newline at end of file diff --git a/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html index c14957b2..418298e9 100644 --- a/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html +++ b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html @@ -67,8 +67,7 @@ Public Member Functions | Protected Member Functions | Protected Attributes | -Static Protected Attributes | -Private Member Functions
+Static Protected Attributes
polar.com.sdk.impl.BDBleApiImpl Class Reference
@@ -100,7 +99,7 @@   boolean isFeatureReady (final String deviceId, int feature)   -void setApiCallback (PolarBleApiCallback callback) +void setApiCallback (PolarBleApiCallback callback)   void setApiLogger (@Nullable PolarBleApiLogger logger)   @@ -169,7 +168,7 @@   abstract void foregroundEntered ()   -abstract void setApiCallback (@Nullable PolarBleApiCallback callback) +abstract void setApiCallback (@Nullable PolarBleApiCallback callback)   abstract void setApiLogger (@Nullable PolarBleApiLogger logger)   @@ -260,7 +259,7 @@   Scheduler scheduler   -PolarBleApiCallback callback +PolarBleApiCallback callback   PolarBleApiLogger logger   @@ -275,11 +274,6 @@ static final int ANDROID_VERSION_O = 26   - - - -

-Private Member Functions

void validate (boolean a) throws PolarInvalidArgument
 
@@ -655,7 +649,7 @@

-

Definition at line 1090 of file BDBleApiImpl.java.

+

Definition at line 1086 of file BDBleApiImpl.java.

@@ -738,7 +732,7 @@

-

Definition at line 1078 of file BDBleApiImpl.java.

+

Definition at line 1074 of file BDBleApiImpl.java.

@@ -832,7 +826,7 @@

-

Definition at line 1122 of file BDBleApiImpl.java.

+

Definition at line 1118 of file BDBleApiImpl.java.

@@ -860,7 +854,7 @@

-

Definition at line 1128 of file BDBleApiImpl.java.

+

Definition at line 1124 of file BDBleApiImpl.java.

@@ -1131,7 +1125,7 @@

-

Definition at line 866 of file BDBleApiImpl.java.

+

Definition at line 870 of file BDBleApiImpl.java.

@@ -1159,7 +1153,7 @@

-

Definition at line 876 of file BDBleApiImpl.java.

+

Definition at line 879 of file BDBleApiImpl.java.

@@ -1187,7 +1181,7 @@

-

Definition at line 907 of file BDBleApiImpl.java.

+

Definition at line 903 of file BDBleApiImpl.java.

@@ -1215,7 +1209,7 @@

-

Definition at line 920 of file BDBleApiImpl.java.

+

Definition at line 916 of file BDBleApiImpl.java.

@@ -1253,7 +1247,7 @@

-

Definition at line 892 of file BDBleApiImpl.java.

+

Definition at line 888 of file BDBleApiImpl.java.

@@ -1269,7 +1263,7 @@

void polar.com.sdk.impl.BDBleApiImpl.setApiCallback

- + @@ -1431,7 +1425,7 @@

-

Definition at line 952 of file BDBleApiImpl.java.

+

Definition at line 948 of file BDBleApiImpl.java.

@@ -1760,7 +1754,7 @@

-

Definition at line 931 of file BDBleApiImpl.java.

+

Definition at line 927 of file BDBleApiImpl.java.

@@ -1790,34 +1784,6 @@

Definition at line 488 of file BDBleApiImpl.java.

- - - -

◆ validate()

- -
-
-

Additional Inherited Members

- Static Public Attributes inherited from polar.com.sdk.api.PolarBleApi
(PolarBleApiCallback PolarBleApiCallback  callback)
- - - - -
- - - - - - - - -
void polar.com.sdk.impl.BDBleApiImpl.validate (boolean a) throws PolarInvalidArgument
-
-inlineprivate
-
- -

Definition at line 886 of file BDBleApiImpl.java.

-

Field Documentation

@@ -1855,7 +1821,7 @@

- +
PolarBleApiCallback polar.com.sdk.impl.BDBleApiImpl.callbackPolarBleApiCallback polar.com.sdk.impl.BDBleApiImpl.callback
diff --git a/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.js b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.js index b28eefe5..7311525e 100644 --- a/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.js +++ b/polar-sdk-android/docs/html/classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.js @@ -46,7 +46,6 @@ var classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl = [ "startRecording", "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a14a4d99e3a14c3e8dd2a9695e25ec9a8", null ], [ "stopPmdStreaming", "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a5e80e959cbc19490c9a1963e6914a092", null ], [ "stopRecording", "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a4eb11a28590fe41d76fdb88c28894ce2", null ], - [ "validate", "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a3116d883037d1dfd712b598f7c02f50e", null ], [ "callback", "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a5ccfe3027f85d5b1262737b1160006b8", null ], [ "connectSubscriptions", "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac6df07abc427e5ad2ba3167a2d8c109b", null ], [ "listener", "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac08998b625def30d377e05abb8a80f8d", null ], diff --git a/polar-sdk-android/docs/html/files_dup.js b/polar-sdk-android/docs/html/files_dup.js index 61887d3f..3240fae1 100644 --- a/polar-sdk-android/docs/html/files_dup.js +++ b/polar-sdk-android/docs/html/files_dup.js @@ -16,7 +16,7 @@ var files_dup = [ "SampleType", "enumpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi_1_1SampleType.html", "enumpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi_1_1SampleType" ] ] ], [ "PolarBleApiCallback.java", "PolarBleApiCallback_8java.html", [ - [ "PolarBleApiCallback", "interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html", "interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback" ] + [ "PolarBleApiCallback", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback" ] ] ], [ "PolarBleApiDefaultImpl.java", "PolarBleApiDefaultImpl_8java.html", [ [ "PolarBleApiDefaultImpl", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiDefaultImpl.html", null ] diff --git a/polar-sdk-android/docs/html/functions.html b/polar-sdk-android/docs/html/functions.html index 23e9f351..5e84bd3f 100644 --- a/polar-sdk-android/docs/html/functions.html +++ b/polar-sdk-android/docs/html/functions.html @@ -66,7 +66,7 @@

- a -

class  PolarBleApi   -interface  PolarBleApiCallback +class  PolarBleApiCallback   class  PolarBleApiDefaultImpl   diff --git a/polar-sdk-android/docs/html/namespacepolar_1_1com_1_1sdk_1_1api.js b/polar-sdk-android/docs/html/namespacepolar_1_1com_1_1sdk_1_1api.js index f55fee90..1f9438b3 100644 --- a/polar-sdk-android/docs/html/namespacepolar_1_1com_1_1sdk_1_1api.js +++ b/polar-sdk-android/docs/html/namespacepolar_1_1com_1_1sdk_1_1api.js @@ -3,6 +3,6 @@ var namespacepolar_1_1com_1_1sdk_1_1api = [ "errors", "namespacepolar_1_1com_1_1sdk_1_1api_1_1errors.html", "namespacepolar_1_1com_1_1sdk_1_1api_1_1errors" ], [ "model", "namespacepolar_1_1com_1_1sdk_1_1api_1_1model.html", "namespacepolar_1_1com_1_1sdk_1_1api_1_1model" ], [ "PolarBleApi", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi.html", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi" ], - [ "PolarBleApiCallback", "interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html", "interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback" ], + [ "PolarBleApiCallback", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback" ], [ "PolarBleApiDefaultImpl", "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiDefaultImpl.html", null ] ]; \ No newline at end of file diff --git a/polar-sdk-android/docs/html/navtreeindex0.js b/polar-sdk-android/docs/html/navtreeindex0.js index 91d73ba9..e8ff693b 100644 --- a/polar-sdk-android/docs/html/navtreeindex0.js +++ b/polar-sdk-android/docs/html/navtreeindex0.js @@ -80,6 +80,21 @@ var NAVTREEINDEX0 = "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi.html#af5e518b8f1ef6ca3c4b1c95f16312b52":[1,0,0,0,0,0,2,8], "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi.html#afa663962cfcfa76ea7140315074ba39f":[1,0,0,0,0,0,2,3], "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi.html#afe25cdd5414973383db68bffc75d492f":[1,0,0,0,0,0,2,34], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html":[1,0,0,0,0,0,3], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a2994bd2e8b3d80b4e4de9ad0bb0920e8":[1,0,0,0,0,0,3,0], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a2e4e8f71a75eebf87be49751fcf4b4e8":[1,0,0,0,0,0,3,3], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a3541b2cec92744126a84ec207f3e1211":[1,0,0,0,0,0,3,12], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a3c2c3ff7eb76a8cba67057b60122b035":[1,0,0,0,0,0,3,8], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a60b5030d4ee50f7cf9d50bcbe2229808":[1,0,0,0,0,0,3,9], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a8d2b40c2a8b2b14d98778f7542e337b8":[1,0,0,0,0,0,3,6], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#aa9534a4ec9f2269d19a112f54d2ec0e0":[1,0,0,0,0,0,3,10], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ab6b0fa75da852019c7b74820e00f2cb9":[1,0,0,0,0,0,3,5], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ad2055f89f0e3a0d1dfce7a444b507698":[1,0,0,0,0,0,3,11], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ad98839ae9719804e7f93db93c03a3502":[1,0,0,0,0,0,3,1], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ae9028fa8d2b8a017db6c01d061a69c8e":[1,0,0,0,0,0,3,7], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ae98b2e7a18f12255b955693e1afc7bf2":[1,0,0,0,0,0,3,4], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#af5ab3ec3f17b4f32085e9e085b0f38c3":[1,0,0,0,0,0,3,13], +"classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#afec6d56715ba82b46129812be452f1ba":[1,0,0,0,0,0,3,2], "classpolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiDefaultImpl.html":[1,0,0,0,0,0,4], "classpolar_1_1com_1_1sdk_1_1api_1_1errors_1_1PolarDeviceDisconnected.html":[1,0,0,0,0,0,0,0], "classpolar_1_1com_1_1sdk_1_1api_1_1errors_1_1PolarDeviceNotConnected.html":[1,0,0,0,0,0,0,1], @@ -153,7 +168,6 @@ var NAVTREEINDEX0 = "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a24707fd78988e8b9f57d47a6d24cc7e3":[1,0,0,0,0,1,0,3], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a2c36d4ab6fbdc804df500d2a1d1c0e5d":[1,0,0,0,0,1,0,32], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a2ca3ebc9ca23822d2aca71a2002c1b73":[1,0,0,0,0,1,0,26], -"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a3116d883037d1dfd712b598f7c02f50e":[1,0,0,0,0,1,0,46], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a31b079f8337863ff89f6f1d6dbce65b9":[1,0,0,0,0,1,0,12], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a34b010c3844f564a377bfb430170e4a9":[1,0,0,0,0,1,0,37], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a436696ba043d2a5d77d5fe33e2ab91cf":[1,0,0,0,0,1,0,24], @@ -165,7 +179,7 @@ var NAVTREEINDEX0 = "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a568cbfd6e9548d54fff470a938cbf000":[1,0,0,0,0,1,0,27], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a573753301ab6af12467792bc87fc19a2":[1,0,0,0,0,1,0,8], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a59c11713e724352b7a8356089185fbda":[1,0,0,0,0,1,0,1], -"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a5ccfe3027f85d5b1262737b1160006b8":[1,0,0,0,0,1,0,47], +"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a5ccfe3027f85d5b1262737b1160006b8":[1,0,0,0,0,1,0,46], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a5d33c2910d8f2583ee44962c4bcac2bf":[1,0,0,0,0,1,0,42], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a5e80e959cbc19490c9a1963e6914a092":[1,0,0,0,0,1,0,44], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#a6170765b10eb5ecc9b6b9584b350fb1a":[1,0,0,0,0,1,0,15], @@ -180,18 +194,18 @@ var NAVTREEINDEX0 = "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#aa7b05d48c5f57c4d66f1e1c182883739":[1,0,0,0,0,1,0,14], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#aae9728d213d59602f3c74b447c83c136":[1,0,0,0,0,1,0,9], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ab7e325a9ea06accea9746866d0118f1b":[1,0,0,0,0,1,0,2], -"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#abb45392febb6f61abfe49ef9482f07bb":[1,0,0,0,0,1,0,50], +"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#abb45392febb6f61abfe49ef9482f07bb":[1,0,0,0,0,1,0,49], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#abc85de0575973be85cc572460952b5d3":[1,0,0,0,0,1,0,21], -"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac08998b625def30d377e05abb8a80f8d":[1,0,0,0,0,1,0,49], +"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac08998b625def30d377e05abb8a80f8d":[1,0,0,0,0,1,0,48], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac08c2703034dc62317eaa6c47ed2284c":[1,0,0,0,0,1,0,34], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac61c2b578af2d5563ef677172196d04d":[1,0,0,0,0,1,0,30], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac645b2eedd096132b613ed5c0b6ecc2f":[1,0,0,0,0,1,0,18], -"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac6df07abc427e5ad2ba3167a2d8c109b":[1,0,0,0,0,1,0,48], +"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ac6df07abc427e5ad2ba3167a2d8c109b":[1,0,0,0,0,1,0,47], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ad239df6d03a59f3a85506346931b52a1":[1,0,0,0,0,1,0,31], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#aded82e5731dc626c167ea7437551ad6d":[1,0,0,0,0,1,0,23], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ae0186ce1014ad60295930835b3620a22":[1,0,0,0,0,1,0,25], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ae1296701871fe33af919ca9423cadd7a":[1,0,0,0,0,1,0,36], -"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ae46e26479d9e1d5c57605c97343fd0f4":[1,0,0,0,0,1,0,51], +"classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ae46e26479d9e1d5c57605c97343fd0f4":[1,0,0,0,0,1,0,50], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#ae897ee6968f1b84641861cda05859a6a":[1,0,0,0,0,1,0,16], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#aea179b27f1dc4b7ed1ad3e2cb971a7a8":[1,0,0,0,0,1,0,7], "classpolar_1_1com_1_1sdk_1_1impl_1_1BDBleApiImpl.html#aed55c3ce811dbb765d761f3b90b8ff96":[1,0,0,0,0,1,0,40], @@ -221,33 +235,19 @@ var NAVTREEINDEX0 = "functions_vars.html":[1,3,2], "hierarchy.html":[1,2], "index.html":[], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html":[1,0,0,0,0,0,3], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a2994bd2e8b3d80b4e4de9ad0bb0920e8":[1,0,0,0,0,0,3,0], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a2e4e8f71a75eebf87be49751fcf4b4e8":[1,0,0,0,0,0,3,3], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a3541b2cec92744126a84ec207f3e1211":[1,0,0,0,0,0,3,12], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a3c2c3ff7eb76a8cba67057b60122b035":[1,0,0,0,0,0,3,8], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a60b5030d4ee50f7cf9d50bcbe2229808":[1,0,0,0,0,0,3,9], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#a8d2b40c2a8b2b14d98778f7542e337b8":[1,0,0,0,0,0,3,6], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#aa9534a4ec9f2269d19a112f54d2ec0e0":[1,0,0,0,0,0,3,10], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ab6b0fa75da852019c7b74820e00f2cb9":[1,0,0,0,0,0,3,5], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ad2055f89f0e3a0d1dfce7a444b507698":[1,0,0,0,0,0,3,11], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ad98839ae9719804e7f93db93c03a3502":[1,0,0,0,0,0,3,1], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ae9028fa8d2b8a017db6c01d061a69c8e":[1,0,0,0,0,0,3,7], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#ae98b2e7a18f12255b955693e1afc7bf2":[1,0,0,0,0,0,3,4], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#af5ab3ec3f17b4f32085e9e085b0f38c3":[1,0,0,0,0,0,3,13], -"interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApiCallback.html#afec6d56715ba82b46129812be452f1ba":[1,0,0,0,0,0,3,2], "interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi_1_1PolarBleApiLogger.html":[1,0,0,0,0,0,2,0], "interfacepolar_1_1com_1_1sdk_1_1api_1_1PolarBleApi_1_1PolarBleApiLogger.html#ae55d724f7a34d52fab2f781993d4efcc":[1,0,0,0,0,0,2,0,0], -"namespacepolar.html":[1,0,0], "namespacepolar.html":[0,0,0], -"namespacepolar_1_1com.html":[0,0,0,0], +"namespacepolar.html":[1,0,0], "namespacepolar_1_1com.html":[1,0,0,0], +"namespacepolar_1_1com.html":[0,0,0,0], "namespacepolar_1_1com_1_1sdk.html":[1,0,0,0,0], "namespacepolar_1_1com_1_1sdk.html":[0,0,0,0,0], "namespacepolar_1_1com_1_1sdk_1_1api.html":[1,0,0,0,0,0], "namespacepolar_1_1com_1_1sdk_1_1api.html":[0,0,0,0,0,0], -"namespacepolar_1_1com_1_1sdk_1_1api_1_1errors.html":[0,0,0,0,0,0,0], "namespacepolar_1_1com_1_1sdk_1_1api_1_1errors.html":[1,0,0,0,0,0,0], +"namespacepolar_1_1com_1_1sdk_1_1api_1_1errors.html":[0,0,0,0,0,0,0], +"namespacepolar_1_1com_1_1sdk_1_1api_1_1model.html":[1,0,0,0,0,0,1], "namespacepolar_1_1com_1_1sdk_1_1api_1_1model.html":[0,0,0,0,0,0,1], -"namespacepolar_1_1com_1_1sdk_1_1api_1_1model.html":[1,0,0,0,0,0,1] +"namespacepolar_1_1com_1_1sdk_1_1impl.html":[1,0,0,0,0,1] }; diff --git a/polar-sdk-android/docs/html/navtreeindex1.js b/polar-sdk-android/docs/html/navtreeindex1.js index 837199d9..71924138 100644 --- a/polar-sdk-android/docs/html/navtreeindex1.js +++ b/polar-sdk-android/docs/html/navtreeindex1.js @@ -1,7 +1,6 @@ var NAVTREEINDEX1 = { "namespacepolar_1_1com_1_1sdk_1_1impl.html":[0,0,0,0,0,1], -"namespacepolar_1_1com_1_1sdk_1_1impl.html":[1,0,0,0,0,1], "namespaces.html":[0,0], "pages.html":[] };