1

Hello I'm using RxAndroidBLE to detect a BLE device. On android 6 >= everything seems to work okay but not on a 4.3 device. My app can only discover the desirable BLE device only once at start. After the device has been discovered no more new discoveries at all until I restart the app. Any advice would be highly appreciated. Below minimum (not)working code example:

MainActivity

import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.util.Log
import com.polidea.rxandroidble.RxBleClient
import com.polidea.rxandroidble.exceptions.BleScanException
import com.polidea.rxandroidble.scan.ScanResult
import com.polidea.rxandroidble.scan.ScanSettings
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        startLeScan(applicationContext)
    }

    private var rxBleClient: RxBleClient? = null
    private var scanSubscription: Subscription? = null

    private var handler: Handler? = null
    private var timer: Timer? = null
    private var timerTask: TimerTask? = null
    private var delay: Int = 0

    private fun isScanning(): Boolean {
        return scanSubscription != null
    }

    fun startLeScan(context: Context) {
        rxBleClient = MyaPP.getRxBleClient(context)

        if (isScanning()) {
            scanSubscription?.unsubscribe()
        } else {
            scanSubscription = rxBleClient?.scanBleDevices(
                    com.polidea.rxandroidble.scan.ScanSettings.Builder()
                            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                            .build())
                    ?.observeOn(AndroidSchedulers.mainThread())
                    //?.doOnNext(this::newDevicesFound)
                    ?.doOnUnsubscribe(this::clearSubscription)
                    ?.subscribe(this::newDevicesFound, this::onScanFailure)
        }

        if(handler == null) {
            handler = Handler()
            timer = Timer(false)
            timerTask = object : TimerTask() {
                override fun run() {
                    handler?.post {
                        if (delay > 7) {
                            delay = 0

                            val service = Executors.newSingleThreadExecutor()
                            service.submit(Runnable {

                                //startLeScan(context)
                            })

                        } else {
                            delay = delay + 1
                        }
                    }
                }
            }
            timer?.scheduleAtFixedRate(timerTask, 0, 300)
        }
    }

    private fun newDevicesFound(devices: ScanResult) {
        Log.d("WHYY??", devices.bleDevice.name)
    }

    fun stopScan() {
        scanSubscription?.unsubscribe()
        destroy()
    }

    private fun clearSubscription() {
        scanSubscription = null
    }

    private fun onScanFailure(throwable: Throwable) {
        if (throwable is BleScanException) {
            handleBleScanException(throwable)
        }
    }

    private fun handleBleScanException(bleScanException: BleScanException) {
        val text: String

        when (bleScanException.reason) {
            BleScanException.BLUETOOTH_NOT_AVAILABLE -> text = "Bluetooth is not available"
            BleScanException.BLUETOOTH_DISABLED -> text = "Enable bluetooth and try again"
            BleScanException.LOCATION_PERMISSION_MISSING -> text = "On Android 6.0 location permission is required. Implement Runtime Permissions"
            BleScanException.LOCATION_SERVICES_DISABLED -> text = "Location services needs to be enabled on Android 6.0"
            BleScanException.SCAN_FAILED_ALREADY_STARTED -> text = "Scan with the same filters is already started"
            BleScanException.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED -> text = "Failed to register application for bluetooth scan"
            BleScanException.SCAN_FAILED_FEATURE_UNSUPPORTED -> text = "Scan with specified parameters is not supported"
            BleScanException.SCAN_FAILED_INTERNAL_ERROR -> text = "Scan failed due to internal error"
            BleScanException.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES -> text = "Scan cannot start due to limited hardware resources"
            BleScanException.UNDOCUMENTED_SCAN_THROTTLE -> text = String.format(
                    Locale.getDefault(),
                    "Android 7+ does not allow more scans. Try in %d seconds",
                    secondsTill(bleScanException.retryDateSuggestion)
            )
            BleScanException.UNKNOWN_ERROR_CODE, BleScanException.BLUETOOTH_CANNOT_START -> text = "Unable to start scanning"
            else -> text = "Unable to start scanning"
        }
        Log.w("EXCEPTION", text, bleScanException)
    }

    private fun secondsTill(retryDateSuggestion: Date?): Long {
        if (retryDateSuggestion != null) {
            return TimeUnit.MILLISECONDS.toSeconds(retryDateSuggestion.time - System.currentTimeMillis())
        }

        return 0
    }

    private fun destroy() {
        timer?.cancel()
        handler?.removeCallbacks(timerTask)
        handler = null
        timerTask = null
        timer = null
    }
}

MyaPP

import android.app.Application
import android.content.Context
import com.polidea.rxandroidble.RxBleClient
import com.polidea.rxandroidble.internal.RxBleLog


class MyaPP: Application() {

    private var rxBleClient: RxBleClient? = null

    companion object {
        fun getRxBleClient(context: Context): RxBleClient? {
            val application = context.applicationContext as MyaPP
            return application.rxBleClient
        }
    }

    override fun onCreate() {
        super.onCreate()

        rxBleClient = RxBleClient.create(this)
        RxBleClient.setLogLevel(RxBleLog.DEBUG)
    }
}

build.gradle

compile "com.polidea.rxandroidble:rxandroidble:1.5.0"
implementation 'io.reactivex:rxandroid:1.2.1'

manifest

<application
        android:name=".MyaPP"
Bob
  • 1,433
  • 1
  • 16
  • 36

1 Answers1

0

Your code looks a lot like the library's sample app (version 1.5.0, branch master-rxjava1). I have checked that recently on Android 4.4.4 which is the oldest I have and it worked fine. There were no API changes between 4.3 and 4.4.

What you may be experiencing is a behaviour specific to your device (feel free to share your phone model) in which it only callbacks for the first time it scans a particular peripheral. There are some threads about this topic already like this one.

Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21