ScanManager.kt

package my.package.ble

import android.bluetooth.BluetoothManager
import android.content.Context
import android.location.LocationManager
import android.util.Log
import androidx.core.location.LocationManagerCompat
import fr.maxcom.beacon.error.ScannerError
import fr.maxcom.beacon.log.LogLevel
import fr.maxcom.beacon.log.Logger
import fr.maxcom.beacon.manager.ProximityManager
import fr.maxcom.beacon.service.IScannerCallback
import fr.maxcom.libbeacon.Licensing
import my.package.BuildConfig

class ScannerManager(private val mContext: Context) {
    private val mProximityManager = ProximityManager(mContext)
    val isBluetoothDisabled: Boolean
        get() {
            val btAdapter = (mContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
            return btAdapter == null || !btAdapter.isEnabled
        }
    val isLocationDisabled: Boolean
        get() = !LocationManagerCompat.isLocationEnabled((mContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager))

    fun requestScanStatus(statusCallback: IStatusCallback) {
        if (BuildConfig.DEBUG) Log.d(TAG, "requestScanStatus")
        mProximityManager.requestScanStatus(object : fr.maxcom.beacon.manager.IStatusCallback {
            override fun onResult(isScanning: Boolean) {
                statusCallback.onResult(isScanning)
            }

            override fun onError(message: String) {
                statusCallback.onError(message)
            }
        })
    }

    fun setUp(statusCallback: IStatusCallback) {
        setUp(Configuration.instance, statusCallback)
    }

    fun setUp(cfg: Configuration, statusCallback: IStatusCallback?) {
        if (BuildConfig.DEBUG) Log.d(TAG, "setUp")
        cfg.clear() // cleanup the stores of a possible previous run
        configureScanner(cfg)
        val listener = cfg.scanStatusListener
        listener?.verbose = cfg.verbose
        val notificationId = if (cfg.activityClass == null) null else NotificationHelper.SERVICE_NOTIFICATION_ID
        val notification = cfg.activityClass?.let { NotificationHelper.createNotification(mContext, it, isLocationDisabled) }
        mProximityManager.setUp(
            listener,
            createScannerCallback(statusCallback),
            null,
            notificationId, notification
        )
    }

    private fun configureScanner(cfg: Configuration) {
        val gc = mProximityManager.configuration()
        gc
            .preventDoze(cfg.preventDoze)
            .verbose(cfg.verbose)
            .scanFrequency(cfg.scanFrequency)

        val interval = cfg.reminderIntervalSeconds
        if (interval > 0) gc.reminderInterval(interval)

        val scanMode = cfg.scanMode
        if (scanMode != null) gc.scanMode(scanMode)

        val rssiCalculator = cfg.rssiCalculator
        if (rssiCalculator != null) gc.rssiCalculator(rssiCalculator)

        val devicesUpdateEventSlowdown = cfg.devicesUpdateEventSlowdown
        if (devicesUpdateEventSlowdown != -1L) gc.devicesUpdateEventSlowdown(devicesUpdateEventSlowdown)

        val registrar = mProximityManager.registrar()
        cfg.processors?.forEach { registrar.registerProcessor(it) }
        cfg.processorPlugins?.forEach { registrar.registerProcessorPlugin(it) }
        cfg.explorerPlugins?.forEach { registrar.registerExplorerPlugin(it) }
        cfg.parserPlugins?.forEach { registrar.registerParserPlugin(it) }
    }

    fun tearDown(statusCallback: IStatusCallback? = null) {
        if (BuildConfig.DEBUG) Log.d(TAG, "tearDown")
        mProximityManager.tearDown(createScannerCallback(statusCallback))
    }

    private fun createScannerCallback(cb: IStatusCallback?): IScannerCallback? {
        return if (cb == null) null else object : IScannerCallback {
            override fun onStarted() {
                if (BuildConfig.DEBUG) Log.d(TAG, "result onStarted")
                cb.onResult(true)
            }

            override fun onStopped() {
                if (BuildConfig.DEBUG) Log.d(TAG, "result onStopped")
                cb.onResult(false)
            }

            override fun onError(error: ScannerError) {
                if (BuildConfig.DEBUG) Log.d(TAG, "result onError $error")
                val message = ErrorCodeMessages[error.ordinal, error.toString()]
                cb.onError(message)
            }
        }
    }

    companion object {
        private val TAG = ScannerManager::class.java.simpleName

        fun init(context: Context) {
            Log.i(TAG, "init")
            Licensing.allow(context) // mandatory call to allow further calls to the library
            Licensing.developerMode = true // log some traces, only useful for a debugging session

            // By default, all levels are enabled, which is helpful for development.
            // For a production release, it is advised to discard low level traces.
            if (!BuildConfig.DEBUG) Logger.mute(true, LogLevel.DEBUG, LogLevel.VERBOSE)
        }
    }
}

ErrorCodeMessages.kt

package my.package.ble

import android.util.SparseArray
import fr.maxcom.beacon.error.ScannerError

/**
 * A static mapping from error codes to human readable strings (for UI and logcat).
 */
object ErrorCodeMessages : SparseArray<String>() {
    init {
        //  from ProximityManager
        append(ScannerError.UNEXPECTED_ONSTARTED.ordinal, "Unexpected onStarted event for a stop request")
        append(ScannerError.OPERATION_ALREADY_IN_PROGRESS.ordinal, "Operation already in progress")
        append(ScannerError.ALREADY_STOPPED.ordinal, "Scan already stopped")
        append(ScannerError.UNABLE_TO_BIND_TO_SERVICE.ordinal, "Unabled to bind to the service")
        append(ScannerError.UNABLE_TO_START_SERVICE.ordinal, "Unabled to start the service")
        // from ProximityService
        append(ScannerError.NO_BT_ADAPTER.ordinal, "No Bluetooth adaptor")
        append(ScannerError.UNEXPECTEDLY_STILL_NOT_RUNNING.ordinal, "Scan unexpectedly still not running")
        append(ScannerError.UNEXPECTEDLY_STILL_RUNNING.ordinal, "Scan unexpectedly still running")
    }
}