首页 > 解决方案 > 在 Kotlin 中获取 BLE 设备

问题描述

SettingsActivity允许用户从配对的蓝牙设备列表中进行选择。

但是,蓝牙低功耗似乎发生了一些奇怪的事情,设备无法正常配对:我尝试连接的设备不会与我的任何 android 设备配对,而且我注意到我的 Fitbit 不是t 是我手机上配对设备的列表,即使它似乎可以正常工作。怎么回事?

无论如何,问题是:如何将 BLE 设备列表添加到正确设备列表中?

(我查看了https://developer.android.com/guide/topics/connectivity/bluetooth-le#find但它只是一些杂乱无章的无法解释的代码;它没有说明将它们放在哪里,如何调用它们,或者它们如何组合在一起,如果我复制它,就会出现大量错误。)

class SettingsActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // get bluetooth devices
        var btDevices: Array<CharSequence> = arrayOf("")
        try {
            val bt: BluetoothManager =
                getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager;
            val bta: BluetoothAdapter = bt.adapter;

            // Get normal Bluetooth devices.
            val pairedDevices: Set<BluetoothDevice> = bta.bondedDevices

            // Get Bluetooth Low Energy devices.

            // HOW?!

            btDevices = pairedDevices.map { z -> z.name }.toTypedArray()
        }
        catch(e:Exception) {}

        // Start the fragment
        setContentView(R.layout.settings_activity)
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.settings, SettingsFragment(cs))
            .commit()
        supportActionBar?.setDisplayHomeAsUpEnabled(true)

        // Slap the toolbar.
        val toolbar = findViewById<Toolbar>(R.id.settings_toolbar) // Must be after setContentView or else it returns null.
        setSupportActionBar(toolbar)
        toolbar.setNavigationOnClickListener(object : View.OnClickListener {
            override fun onClick(v: View?) {
                finish()
            }
        })
    }

    class SettingsFragment(adapters: Array<CharSequence>) : PreferenceFragmentCompat() {
        private var adapters: Array<CharSequence> = adapters

        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
           setPreferencesFromResource(R.xml.root_preferences, rootKey)

            val p:ListPreference? = findPreference<ListPreference>("bluetoothName")
            p?.setEntries(adapters)
            p?.setEntryValues(adapters)
        }
    }
}

标签: kotlinandroid-ble

解决方案


我确信这是非常错误的,但它似乎确实有效。

它的诀窍是将引用添加到PreferenceFragment公共方法以更新列表,然后将引用传递PreferencesFragment给 theScanCallback以便它可以将其更新的列表发送到首选项。

package com.rwb.psamfd

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat

class SettingsActivity : AppCompatActivity() {

    private val bleScanCallback = @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    object : ScanCallback(){
        public var settings:SettingsFragment? = null;

        override fun onScanResult(callbackType: Int, result: ScanResult?) {
            super.onScanResult(callbackType, result)
            Log.d(
                "DeviceListActivity",
                "onScanResult: ${result?.device?.address} - ${result?.device?.name}"
            )
            if (result?.device?.name != null && !btAdapters.contains(result?.device?.name!!)) {
                btAdapters.add(result?.device?.name!!)
            }
            settings?.updateAdapters(btAdapters)
        }

        override fun onBatchScanResults(results: MutableList<ScanResult>?) {
            super.onBatchScanResults(results)
            Log.d("DeviceListActivity","onBatchScanResults:${results.toString()}")
            if(results != null){
                for(result:ScanResult in results!!)
                {
                    if(result.device?.name != null && !btAdapters.contains(result.device?.name!! ))
                    {
                        btAdapters.add(result.device?.name!! )
                    }
                }
                settings?.updateAdapters(btAdapters)
            }
        }

        override fun onScanFailed(errorCode: Int) {
            super.onScanFailed(errorCode)
            Log.d("DeviceListActivity", "onScanFailed: $errorCode")
        }
    }

    private lateinit var btm : BluetoothManager
    private lateinit var bta: BluetoothAdapter

    // This is initialised from paired normal Bluetooth, then added to by BLE.
    // BLE has a reference to the preferences fragment on which it calles the update method when new devices are found.
    private val btAdapters: ArrayList<String> = ArrayList<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        btm = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager;
        bta = btm.adapter

        // get bluetooth devices
        try {
            // Normal Bluetooth.
            for(p:BluetoothDevice in bta.bondedDevices)
            {
                if(p.name != null) {
                    btAdapters.add(p.name)
                }
            }

            // Bluetooth Low Energy done in onResume and onPause.
        }
        catch(e:Exception) {}

        // Start the fragment
        val sf:SettingsFragment = SettingsFragment(btAdapters)

        setContentView(R.layout.settings_activity)
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.settings, sf)
            .commit()
        supportActionBar?.setDisplayHomeAsUpEnabled(true)

        // Connect the SettingsFragment to the ScanCallback.
        bleScanCallback.settings = sf

        // Slap the toolbar.
        val toolbar = findViewById<Toolbar>(R.id.settings_toolbar) // Must be after setContentView or else it returns null.
        setSupportActionBar(toolbar)
        toolbar.setNavigationOnClickListener(object : View.OnClickListener {
            override fun onClick(v: View?) {
                finish()
            }
        })
    }

    override fun onResume() {
        super.onResume()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            bta.bluetoothLeScanner.startScan(bleScanCallback)
        }
    }

    override fun onPause() {
        super.onPause()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            bta.bluetoothLeScanner.stopScan(bleScanCallback)
        }
    }

    class SettingsFragment(btAdapters: ArrayList<String>) : PreferenceFragmentCompat() {
        private var btAdapters: Array<CharSequence> = btAdapters.map{z -> z as CharSequence}.toTypedArray()
        private lateinit var btPreference : ListPreference

        fun updateAdapters(newBtAdapters: ArrayList<String>){
            btAdapters = newBtAdapters.map{z -> z as CharSequence}.toTypedArray()
            btPreference.setEntries(btAdapters)
            btPreference.setEntryValues(btAdapters)
        }

        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
           setPreferencesFromResource(R.xml.root_preferences, rootKey)

            btPreference = findPreference<ListPreference>("bluetoothName")!!
            btPreference.setEntries(btAdapters)
            btPreference.setEntryValues(btAdapters)
        }
    }
}

推荐阅读