Получите устройства BLE в Kotlin

У меня есть SettingsActivity, позволяющий пользователю выбирать из списка сопряженных устройств Bluetooth.

Тем не менее, кажется, что с Bluetooth Low Energy происходит что-то странное, когда устройства не соединяются нормально: устройство, к которому я пытаюсь подключиться, не будет соединяться ни с одним из моих устройств Android, и я замечаю, что мой Fitbit не работает. Это список сопряженных устройств на моем телефоне, хотя кажется, что он работает. ВТФ?

В любом случае, вопрос: как мне добавить в список соответствующих устройств список устройств 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)
        }
    }
}

person Richard Barraclough    schedule 16.06.2020    source источник
comment
Похоже, это должно быть что-то вроде: bta.bluetoothLeScanner.startScan(bleScanCallback) `// ждем сканирования` `//добавляем имена устройств в список`, где bleScanCallback : ScanCallback   -  person Richard Barraclough    schedule 16.06.2020
comment
Почему уценка нарушена?   -  person Richard Barraclough    schedule 16.06.2020


Ответы (1)


Я уверен, что это очень неправильно, но похоже это работает.

Хитрость заключается в том, чтобы поместить ссылку на добавление в PreferenceFragment общедоступного метода для обновления списка, а затем передать ссылку PreferencesFragment в ScanCallback, чтобы он мог отправить свой обновленный список в настройки.

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)
        }
    }
}
person Richard Barraclough    schedule 16.06.2020