diff --git a/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/api/ble/BleDeviceListener.java b/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/api/ble/BleDeviceListener.java index 85d8ef66..b10710fa 100755 --- a/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/api/ble/BleDeviceListener.java +++ b/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/api/ble/BleDeviceListener.java @@ -72,6 +72,11 @@ public interface BlePowerStateChangedCallback { void stateChanged(boolean power); } + /** + * Restarts the scan + */ + public abstract void scanRestart(); + /** * @param filters scan filter list, android specific */ diff --git a/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/enpoints/ble/bluedroid/host/BDDeviceListenerImpl.java b/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/enpoints/ble/bluedroid/host/BDDeviceListenerImpl.java index 3a3bf1ec..080a4c4c 100755 --- a/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/enpoints/ble/bluedroid/host/BDDeviceListenerImpl.java +++ b/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/enpoints/ble/bluedroid/host/BDDeviceListenerImpl.java @@ -87,6 +87,11 @@ public boolean bleActive() { return bluetoothAdapter != null && bluetoothAdapter.isEnabled(); } + @Override + public void scanRestart() { + scanCallback.scanRestart(); + } + @Override public void setScanFilters(@Nullable List filters) { scanCallback.setScanFilters(filters); @@ -270,6 +275,7 @@ public boolean isScanningNeeded() { @Override public void scanStartError(@NonNull String error) { + BleLogger.e(TAG, "scanStartError " + error); RxUtils.postError(observers, new BleStartScanError(error)); } diff --git a/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/enpoints/ble/bluedroid/host/BDScanCallback.kt b/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/enpoints/ble/bluedroid/host/BDScanCallback.kt index e63be4c7..360069a8 100755 --- a/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/enpoints/ble/bluedroid/host/BDScanCallback.kt +++ b/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/enpoints/ble/bluedroid/host/BDScanCallback.kt @@ -8,14 +8,16 @@ import android.bluetooth.le.ScanResult import android.bluetooth.le.ScanSettings import android.content.Context import android.os.Build +import android.os.ParcelUuid import com.polar.androidcommunications.api.ble.BleLogger +import com.polar.androidcommunications.api.ble.model.gatt.client.BleHrClient +import com.polar.androidcommunications.api.ble.model.gatt.client.psftp.BlePsFtpUtils import com.polar.androidcommunications.common.ble.BleUtils.EVENT_TYPE import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Scheduler import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.schedulers.Schedulers -import java.util.* import java.util.concurrent.TimeUnit internal class BDScanCallback( @@ -23,6 +25,23 @@ internal class BDScanCallback( private val bluetoothAdapter: BluetoothAdapter, private val scanCallbackInterface: BDScanCallbackInterface ) { + companion object { + private const val TAG = "BDScanCallback" + private const val POLAR_MANUFACTURER_ID = 0x006b + + // scan window limit, for android's "is scanning too frequently" + private const val SCAN_WINDOW_LIMIT = 30000 + } + + private var scanFilter: List? = null + + init { + val defaultFilter: MutableList = ArrayList() + defaultFilter.add(ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(BleHrClient.HR_SERVICE.toString())).build()) + defaultFilter.add(ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(BlePsFtpUtils.RFC77_PFTP_SERVICE.toString())).build()) + defaultFilter.add(ScanFilter.Builder().setManufacturerData(POLAR_MANUFACTURER_ID, byteArrayOf()).build()) + this.scanFilter = defaultFilter + } private enum class ScanAction { ENTRY, @@ -47,7 +66,7 @@ internal class BDScanCallback( var lowPowerEnabled = false var opportunistic = true private var adminStops = 0 - private var filters: List? = null + private var delaySubscription: Disposable? = null private var opportunisticScanTimer: Disposable? = null @@ -59,7 +78,12 @@ internal class BDScanCallback( fun setScanFilters(filters: List?) { stopScan() - this.filters = filters + this.scanFilter = filters + startScan() + } + + fun scanRestart() { + stopScan() startScan() } @@ -297,7 +321,7 @@ internal class BDScanCallback( private fun callStartScanL(scanSettings: ScanSettings) { try { - bluetoothAdapter.bluetoothLeScanner.startScan(filters, scanSettings, leScanCallback) + bluetoothAdapter.bluetoothLeScanner.startScan(scanFilter, scanSettings, leScanCallback) } catch (e: Exception) { val errorString = "Failed to start scan. Reason: ${e.message}" BleLogger.e(TAG, errorString) @@ -324,10 +348,5 @@ internal class BDScanCallback( } } - companion object { - private val TAG = BDScanCallback::class.java.simpleName - // scan window limit, for android's "is scanning too frequently" - private const val SCAN_WINDOW_LIMIT = 30000 - } } \ No newline at end of file diff --git a/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/PolarBleApi.java b/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/PolarBleApi.java index 98c81912..ecfee2c7 100644 --- a/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/PolarBleApi.java +++ b/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/PolarBleApi.java @@ -135,7 +135,7 @@ protected PolarBleApi(final int features) { * the android component (e.g. Activity or Service) then the shutDown may be called * on component destroy function. After shutDown the new instance of the SDK is needed: * - * @see PolarBleApiDefaultImpl#defaultImplementation + * @see PolarBleApiDefaultImpl#defaultImplementation(Context, int) */ public abstract void shutDown(); @@ -145,7 +145,11 @@ protected PolarBleApi(final int features) { public abstract void cleanup(); /** - * @param enable false disable polar filter which means in all apis identifier can be bt address too + * When enabled only Polar devices are found by the {@link #searchForDevice()}, if set to false + * any BLE devices with HR services are returned by the {@link #searchForDevice()}. The default setting for + * Polar filter is true. + * + * @param enable false disables polar filter */ public abstract void setPolarFilter(boolean enable); @@ -161,11 +165,18 @@ protected PolarBleApi(final int features) { /** * enables scan filter while on background + * + * @deprecated in release 3.2.8. Move to the background is not relevant information for SDK starting from release 3.2.8 */ + @Deprecated public abstract void backgroundEntered(); /** - * disables scan filter while on foreground + * Optionally call when application enters to the foreground. By calling foregroundEntered() you make + * sure BLE scan is restarted. BLE scan start is not working when Android device display is off + * (related to Android power save). By calling foregroundEntered() helps in some rare situations + * e.g. if connection is lost to the device and {@link #setAutomaticReconnection(boolean)} is enabled, + * reconnection is created when application is back in foreground. */ public abstract void foregroundEntered(); @@ -184,7 +195,7 @@ protected PolarBleApi(final int features) { public abstract void setApiLogger(@NonNull PolarBleApiLogger logger); /** - * Enable or disable automatic reconnection feature + * When enabled the reconnection is attempted if device connection is lost. By default automatic reconnection is enabled. * * @param enable true = automatic reconnection is enabled, false = automatic reconnection is disabled */ @@ -248,7 +259,7 @@ public abstract Single requestFullStreamSettings(@NonNull fi public abstract Completable autoConnectToDevice(int rssiLimit, @Nullable String service, @Nullable final String polarDeviceType); /** - * Request a connection to a Polar device. Invokes {@link PolarBleApiCallback#deviceConnected(PolarDeviceInfo)} callback. + * Request a connection to a BLE device. Invokes {@link PolarBleApiCallback#deviceConnected(PolarDeviceInfo)} callback. * * @param identifier Polar device id found printed on the sensor/device (in format "12345678") * or bt address (in format "00:11:22:33:44:55") @@ -257,9 +268,9 @@ public abstract Single requestFullStreamSettings(@NonNull fi public abstract void connectToDevice(@NonNull final String identifier) throws PolarInvalidArgument; /** - * Request disconnecting from a Polar device. Invokes {@link PolarBleApiCallback#deviceDisconnected(PolarDeviceInfo)} callback. + * Request disconnecting from a BLE device. Invokes {@link PolarBleApiCallback#deviceDisconnected(PolarDeviceInfo)} callback. * - * @param identifier Polar device id found printed on the sensor/device or bt address + * @param identifier Polar device id found printed on the sensor/device or bt address (in format "00:11:22:33:44:55") * @throws PolarInvalidArgument if identifier is invalid formatted mac address or polar device id */ public abstract void disconnectFromDevice(@NonNull final String identifier) throws PolarInvalidArgument; @@ -339,11 +350,14 @@ public abstract Completable startRecording(@NonNull final String identifier, public abstract Completable removeExercise(@NonNull final String identifier, @NonNull final PolarExerciseEntry entry); /** - * Start searching for device(s) + * Starts searching for BLE devices when subscribed. Search continues as long as observable is + * subscribed or error. Each found device is emitted only once. By default searches only for Polar devices, + * but can be controlled by {@link #setPolarFilter(boolean)}. If {@link #setPolarFilter(boolean)} is false + * then searches for any BLE heart rate capable devices * * @return Flowable stream of {@link PolarDeviceInfo} * Produces: - *
- onNext for any new Polar device detected + *
- onNext for any new Polar (or BLE) device detected *
- onError if scan start fails *
- onComplete non produced unless stream is further configured */ @@ -351,7 +365,9 @@ public abstract Completable startRecording(@NonNull final String identifier, public abstract Flowable searchForDevice(); /** - * Start listening to heart rate broadcasts from one or more Polar devices + * Start listening the heart rate from Polar devices when subscribed. This observable listens BLE + * broadcast and parses heart rate from BLE broadcast. The BLE device is not connected when + * using this function. * * @param deviceIds set of Polar device ids to filter or null for a any Polar device * @return Flowable stream of {@link PolarHrBroadcastData} diff --git a/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/model/PolarHrBroadcastData.java b/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/model/PolarHrBroadcastData.java index b55a41b0..4d63fe51 100644 --- a/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/model/PolarHrBroadcastData.java +++ b/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/model/PolarHrBroadcastData.java @@ -7,7 +7,9 @@ public class PolarHrBroadcastData { /** - * @see polar.com.sdk.api.model.PolarDeviceInfo + * Device information + * + * @see PolarDeviceInfo */ public final PolarDeviceInfo polarDeviceInfo; diff --git a/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/impl/BDBleApiImpl.java b/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/impl/BDBleApiImpl.java index ff22872f..ece465f0 100644 --- a/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/impl/BDBleApiImpl.java +++ b/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/impl/BDBleApiImpl.java @@ -7,10 +7,7 @@ import static com.polar.androidcommunications.api.ble.model.gatt.client.pmd.PmdControlPointResponse.PmdControlPointResponseCode.ERROR_ALREADY_IN_STATE; import android.annotation.SuppressLint; -import android.bluetooth.le.ScanFilter; import android.content.Context; -import android.os.Build; -import android.os.ParcelUuid; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -102,7 +99,7 @@ * The default implementation of the Polar API */ public class BDBleApiImpl extends PolarBleApi implements BleDeviceListener.BlePowerStateChangedCallback { - + private static final String TAG = "BDBleApiImpl"; private static volatile BDBleApiImpl instance; private final Map connectSubscriptions = new HashMap<>(); private final Map deviceDataMonitorDisposable = new HashMap<>(); @@ -178,17 +175,6 @@ private static void clearInstance() { instance = null; } - private void enableAndroidScanFilter() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - List scanFilter = new ArrayList<>(); - scanFilter.add(new ScanFilter.Builder().setServiceUuid( - ParcelUuid.fromString(BleHrClient.HR_SERVICE.toString())).build()); - scanFilter.add(new ScanFilter.Builder().setServiceUuid( - ParcelUuid.fromString(BlePsFtpUtils.RFC77_PFTP_SERVICE.toString())).build()); - listener.setScanFilters(scanFilter); - } - } - @Override public void setMtu(int mtu) { listener.setMtu(mtu); @@ -366,12 +352,12 @@ private Single queryFullSettings(final String identifier, fi @Override public void backgroundEntered() { - enableAndroidScanFilter(); + BleLogger.w(TAG, "call of deprecated backgroundEntered() method"); } @Override public void foregroundEntered() { - listener.setScanFilters(null); + listener.scanRestart(); } @NonNull