1

in my android repository layer I am holding a reference to the Android RxBleConnection. I also have a read write function that return an observable, my problem is that this function is being called but not it is not responding because the connection observable has stopped emitting, you can check the code bellow, any suggestions ?

class LampRepository(private val lampBleDevice: Observable<RxBleDevice>,
                 private val bluetoothExecutor: Executor) : IRepository {

companion object {
   private val TAG = "LampRepository"
}

 val  lampConnectionState:Observable<RxBleConnection.RxBleConnectionState>
 val  lampBleConnection:Observable<RxBleConnection>

init {

      lampBleConnection = lampBleDevice.subscribeOn(Schedulers.from(bluetoothExecutor))
                                      .observeOn(AndroidSchedulers.mainThread())
                                      .flatMap {
                                            Log.d(TAG,"Connecting to the lamp GATT server")
                                           it.establishConnection(true) }
                                      .compose(ReplayingShare.instance())


    lampConnectionState =  lampBleDevice.subscribeOn(Schedulers.from(bluetoothExecutor))
                                        .observeOn(AndroidSchedulers.mainThread())
                                        .flatMap {
                                                    Log.d(TAG,"Observing the Lamp GATT server connection state")
                                                    it.observeConnectionStateChanges()}
                                        .share()
}


fun getLampLuminosityLevel()
    = lampBleConnection.subscribeOn(Schedulers.from(bluetoothExecutor))
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap {
            Log.d(TAG,"Reading the lamp luminosity characteristic")
            it.readCharacteristic(UUID.fromString(LampProfile.STATE_CHARACTERISTIC_UUID))
                    ?.toObservable()}
        .flatMap {
            Observable.just(it[0].toInt()) }
        .flatMap { Observable.just(0) }
        .flatMap {
            when (it) {
                0 -> Observable.just(LampProfile.Luminosity.NON)
                1 -> Observable.just(LampProfile.Luminosity.LOW)
                2 -> Observable.just(LampProfile.Luminosity.MEDIUM)
                3 -> Observable.just(LampProfile.Luminosity.HIGH)
                4 -> Observable.just(LampProfile.Luminosity.MAX)
                else -> Observable.error(Throwable("unknown value ${it}"))
            }}

fun getLampPowerState()
    = lampBleConnection.subscribeOn(Schedulers.from(bluetoothExecutor))
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap {
            Log.d(TAG,"Reading the lamp power state characteristic")
            it.readCharacteristic(UUID.fromString(LampProfile.STATE_CHARACTERISTIC_UUID))
                    .toObservable()}
        .flatMap {
            Observable.just(it[0].toInt()) }
        .flatMap {
            when (it) {
                0 -> Observable.just(LampProfile.State.OFF)
                1 ->  Observable.just(LampProfile.State.ON)
                else ->  Observable.error(Throwable("unknown value ${it}"))
            }}


fun setLampPowerState(state: LampProfile.State)
        = lampBleConnection.subscribeOn(Schedulers.from(bluetoothExecutor))
                        .observeOn(AndroidSchedulers.mainThread())
                         .flatMap {
                                    Log.d(TAG,"writing to the Characteristic")
                                 it.writeCharacteristic(UUID.fromString(LampProfile.STATE_CHARACTERISTIC_UUID), byteArrayOf(state.value.toByte()))
                                   .toObservable()}
                        .flatMap {
                            Observable.just(it[0].toInt()) }
                         .flatMap {  Observable.just(1) }
                        .flatMap {
                            Log.d(TAG,"Finish writing")
                            when (it) {
                                0 -> Observable.just(LampProfile.State.OFF)
                                1 ->  Observable.just(LampProfile.State.ON)
                                else -> Observable.error(Throwable("unknown value")) }}


fun setLampLuminosityLevel(level: LampProfile.Luminosity)
      =lampBleConnection.subscribeOn(Schedulers.from(bluetoothExecutor))
            .flatMap {
                Log.d(TAG,"Writing the lamp luminosity characteristic")
                it.writeCharacteristic(UUID.fromString(LampProfile.LUMINOSITY_CHARACTERISTIC_UUID), byteArrayOf(level.value.toByte()))
                        .toObservable()
            }
            .flatMap {
                Observable.just(it[0].toInt())
            }
            .flatMap { Observable.just(0) }
            .flatMap {
                    when (it) {
                        0 -> Observable.just(LampProfile.Luminosity.NON)
                        1 -> Observable.just(LampProfile.Luminosity.LOW)
                        2 -> Observable.just(LampProfile.Luminosity.MEDIUM)
                        3 -> Observable.just(LampProfile.Luminosity.HIGH)
                        4 -> Observable.just(LampProfile.Luminosity.MAX)
                        else -> Observable.error(Throwable("unknown value"))
                    }
            }


}

class LampViewModel(private val lampRepository:LampRepository):ViewModel(){

companion object {
    val TAG = "LampViewModel"
}
private val  mLampPowerState:MutableLiveData<LampProfile.State> = MutableLiveData()
private val  mLampLuminosityLevel:MutableLiveData<LampProfile.Luminosity>  = MutableLiveData()
private val  mLampBleConnectionState:MutableLiveData<RxBleConnection.RxBleConnectionState>  = MutableLiveData()
private val  compositeDisposable = CompositeDisposable()

init {

    compositeDisposable.add(lampRepository.lampConnectionState.subscribe(
          mLampBleConnectionState::postValue,{
          Log.d(TAG,"error is ${it.message}")
  }))

    compositeDisposable.add(lampRepository.lampBleConnection.subscribe({
    },{

    }))


}


fun getLampLuminosityLevel():LiveData<LampProfile.Luminosity> = mLampLuminosityLevel
fun getLampPowerState():LiveData<LampProfile.State> = mLampPowerState
fun getLampConnectionState():LiveData<RxBleConnection.RxBleConnectionState> = mLampBleConnectionState



fun setLampPowerState(state: LampProfile.State) {
    Log.d(TAG,"Into the function")
  compositeDisposable.add(lampRepository.setLampPowerState(state)
        .subscribe({
            Log.d(TAG,"writing with success $it")
        },{
            Log.d(TAG,"error while writing ${it.message}")
        }))

}
fun setLampLuminosityLevel(level: LampProfile.Luminosity) {
    compositeDisposable.add(lampRepository.setLampLuminosityLevel(level)
            .subscribe({

            },{
                Log.d(TAG,"writing error")
            }))

}

class DefaultServiceLocator (private val activity: FragmentActivity): ServiceLocator {

companion object {
  private  val TAG = "DefaultServiceLocator"
}


private var blueToothClient = RxBleClient.create(activity)
private var rxPermissions = RxPermissions(activity)

private val BLUETOOTH_IO = Executors.newFixedThreadPool(2)
private val NETWORK_IO = Executors.newFixedThreadPool(1)
private val bluetoothScan:Observable<ScanResult>
private val bluetoothClientState:Observable<RxBleClient.State>
private val lampBleDevice: Observable<RxBleDevice>
private val broadLinkBleDevice: Observable<RxBleDevice>
private val broadLinkRepository:BroadLinkRepository
private val mConfig = AIConfiguration(
        "e87e26ceb2ae4519ace2f3c71abd076e",
                         ai.api.AIConfiguration.SupportedLanguages.English,
                         AIConfiguration.RecognitionEngine.System
)
private val AIDataService = AIDataService(mConfig)

init {




    bluetoothClientState =  blueToothClient.observeStateChanges()
                                           .observeOn(Schedulers.from(getBlueToothExecutor()))
                                           .subscribeOn(AndroidSchedulers.mainThread())
                                           .startWith(Observable.just(blueToothClient.state))
                                           .share()


     bluetoothScan =  bluetoothClientState.filter{it == RxBleClient.State.READY}
             .delay(1000,TimeUnit.MILLISECONDS)
             .flatMap {
                blueToothClient.scanBleDevices(
                        ScanSettings.Builder()
                                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                                .setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
                                .build(),
                        ScanFilter.Builder().build())}
            .share()



    broadLinkBleDevice = bluetoothScan.filter { it.bleDevice.macAddress ==  BroadLinkProfile.DEVICE_MAC_ADDRESS}
                                      .flatMap { result ->
                                                       Log.d(TAG, "the device named ${result.bleDevice.name} is found")
                                              Observable.just(result.bleDevice) }
                                      .retry()
                                      .share()


    lampBleDevice =   bluetoothScan.filter { it.bleDevice.macAddress == LampProfile.DEVICE_MAC_ADDRESS }
                                   .flatMap { result ->
                                                  Log.d(TAG, "the device named ${result.bleDevice.name} is found")
                                          Observable.just(result.bleDevice) }
                                   .retry()
                                   .share()


    broadLinkRepository = BroadLinkRepository(broadLinkBleDevice,getBlueToothExecutor())



}



override fun getBlueToothExecutor() = BLUETOOTH_IO

override fun getNetworkExecutor() = NETWORK_IO

override fun getBleDevice(key: BleDevices): Observable<RxBleDevice> {

    return when (key) {
        BleDevices.LAMP -> lampBleDevice
        BleDevices.BROAD_LINK -> broadLinkBleDevice
    }
}

override fun getRepository(key: Repositories): IRepository {
    return when (key) {
        Repositories.LAMP_REPOSITORY -> LampRepository(
                lampBleDevice = getBleDevice(BleDevices.LAMP),
                bluetoothExecutor = getBlueToothExecutor()
        )

        Repositories.TV_REPOSITORY -> TvRepository(
                broadLinkRepository = broadLinkRepository,
                bluetoothExecutor = getBlueToothExecutor()

        )
        Repositories.AIR_CONDITIONER_REPOSITORY -> AirConditionerRepository(
                broadLinkRepository = broadLinkRepository,
                bluetoothExecutor = getBlueToothExecutor()
        )
        Repositories.DIALOG_REPOSITORY -> DialogRepository(
                AIService = AIDataService,
                networkExecutor = getNetworkExecutor()

        )
    }
}

}

azzeddine
  • 41
  • 5

1 Answers1

0

The lampBleConnection will emit only once per connection since you are using both ReplayingShare and .share().

The ReplayingShare shares the upstream Observable subscription and emit's the last event to new subscribers. Using .share() does partially the same thing as it also shares the upstream Observable subscription rendering the ReplayingShare useless as it will be subscribed only once.

Bottom line — remove .share() from the lampBleConnection and from setLampPowerState()

The (original) below code with removed .share() should do the trick:

class LampRepository(private val lampBleDevice: Observable<RxBleDevice>,
    private val bluetoothExecutor: Executor) : IRepository {

    companion object {
        private val TAG = "LampRepository"
    }

    val  lampConnectionState:Observable<RxBleConnection.RxBleConnectionState>
    val  lampBleConnection:Observable<RxBleConnection>

    init {

        lampBleConnection = lampBleDevice.subscribeOn(Schedulers.from(bluetoothExecutor))
                              .observeOn(AndroidSchedulers.mainThread())
                              .flatMap {
                                            Log.d(TAG,"Connecting to the lamp GATT server")
                                  it.establishConnection(true) }
                              .compose(ReplayingShare.instance())


        lampConnectionState =  lampBleDevice.subscribeOn(Schedulers.from(bluetoothExecutor))
                                .observeOn(AndroidSchedulers.mainThread())
                                .flatMap {
                                            Log.d(TAG,"Observing the Lamp GATT server connection state")
                                            it.observeConnectionStateChanges()}
                                .share()

    }


    fun getLampLuminosityLevel() = lampBleConnection.subscribeOn(Schedulers.from(bluetoothExecutor))
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap {
            Log.d(TAG,"Reading the lamp luminosity characteristic")
            it.readCharacteristic(UUID.fromString(LampProfile.STATE_CHARACTERISTIC_UUID))
                ?.toObservable()
        }
        .flatMap { Observable.just(it[0].toInt()) }
        .flatMap { Observable.just(0) }
        .flatMap {
            when (it) {
                0 -> Observable.just(LampProfile.Luminosity.NON)
                1 -> Observable.just(LampProfile.Luminosity.LOW)
                2 -> Observable.just(LampProfile.Luminosity.MEDIUM)
                3 -> Observable.just(LampProfile.Luminosity.HIGH)
                4 -> Observable.just(LampProfile.Luminosity.MAX)
                else -> Observable.error(Throwable("unknown value ${it}"))
            }
        }

    fun getLampPowerState() = lampBleConnection.subscribeOn(Schedulers.from(bluetoothExecutor))
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap {
            Log.d(TAG,"Reading the lamp power state characteristic")
            it.readCharacteristic(UUID.fromString(LampProfile.STATE_CHARACTERISTIC_UUID))
                .toObservable()
          }
        .flatMap { Observable.just(it[0].toInt()) }
        .flatMap { Observable.just(0) }
        .flatMap {
            when (it) {
                0 -> Observable.just(LampProfile.State.OFF)
                1 ->  Observable.just(LampProfile.State.ON)
                else ->  Observable.error(Throwable("unknown value ${it}"))
            }
        }

    fun setLampPowerState(state: LampProfile.State) = lampBleConnection.subscribeOn(Schedulers.from(bluetoothExecutor))
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap {
            Log.d(TAG,"writing to the Characteristic")
            it.writeCharacteristic(UUID.fromString(LampProfile.STATE_CHARACTERISTIC_UUID), byteArrayOf(state.value.toByte()))
                ?.toObservable()
        }
        .flatMap { Observable.just(it[0].toInt()) }
        .flatMap {
            Log.d(TAG,"Finish writing")
            when (it) {
                0 -> Observable.just(LampProfile.State.OFF)
                1 ->  Observable.just(LampProfile.State.ON)
                else -> Observable.error(Throwable("unknown value")) 
            }
        }


    fun setLampLuminosityLevel(level: LampProfile.Luminosity) = lampBleConnection.subscribeOn(Schedulers.from(bluetoothExecutor))
        .flatMap {
            Log.d(TAG,"Writing the lamp luminosity characteristic")
            it.writeCharacteristic(UUID.fromString(LampProfile.LUMINOSITY_CHARACTERISTIC_UUID), byteArrayOf(level.value.toByte()))
                    .toObservable()
        }
        .flatMap { Observable.just(it[0].toInt()) }
        .flatMap { Observable.just(0) }
        .flatMap {
            when (it) {
                0 -> Observable.just(LampProfile.Luminosity.NON)
                1 -> Observable.just(LampProfile.Luminosity.LOW)
                2 -> Observable.just(LampProfile.Luminosity.MEDIUM)
                3 -> Observable.just(LampProfile.Luminosity.HIGH)
                4 -> Observable.just(LampProfile.Luminosity.MAX)
                else -> Observable.error(Throwable("unknown value"))
            }
        }
}
Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21
  • Thanks for replying, I have multiple read and write functions like setLampPowerState(), is that Ok? – azzeddine Jul 16 '18 at 10:06
  • The connection `Observable` is shared (`ReplayingShare`) so it should be fine as long as the rest of the communication is stateless – Dariusz Seweryn Jul 16 '18 at 10:33
  • Can you please, show me the code you are suggesting, you will do me a big favor !!! I have edited my question, I added all the repository class code :) – azzeddine Jul 16 '18 at 11:04
  • Updated answer. Feel free to check it out. – Dariusz Seweryn Jul 16 '18 at 12:04
  • Sorry, the RxBleConnection is still not emitting when calling one if the read/write function from the viewModel where I am also suppose to be subscribing to the response of these functions, I am providing also the code of the view model with the updated question above. thank you! – azzeddine Jul 16 '18 at 12:11
  • You have tried to edit my answer — please add the additional code to your question. – Dariusz Seweryn Jul 16 '18 at 12:53
  • Are you sure that the connection is even established? The above code should work. Use `it.establishConnection(false)` to make the connection success (or fail) within 30 seconds. – Dariusz Seweryn Jul 16 '18 at 13:03
  • Unfortunately, the new code didn't work also :/, I added also the code of the ServiceLocator class that it is used to inject the scanned BLE device – azzeddine Jul 16 '18 at 13:09
  • This is getting bigger and bigger. This seem not to be a problem with the usage of library and/or RxJava but rather an incorrect implementation. I can suggest you two more things: 1. Add logs from the console so it will be possible to tell what exactly is going on 2. Try to boil down the whole code to a minimal snippet that does not work – Dariusz Seweryn Jul 16 '18 at 13:28
  • is there a way how I can keep the last emitted value by the RxBLEConnection observable, I've tried using relaySharing and refCount with replay but nothing works ? – azzeddine Jul 17 '18 at 09:49
  • The [`ReplayingShare`](https://github.com/JakeWharton/RxReplayingShare#rxjava-replaying-share) should be exactly what you need. If it does not work for you then there seems to be a different problem in how `RxJava` is used in the code. Try to split the code into small chunks and test them in isolation until the culprit is found – Dariusz Seweryn Jul 17 '18 at 10:18