Сканируйте устройство BLE фрагментарно в Android Studio — Kotlin

#android #kotlin #android-fragments #bluetooth #bluetooth-lowenergy

Вопрос:

В течение нескольких дней я пытаюсь реализовать подключение BLE в своем приложении. Я делаю это во второстепенном фрагменте, а не в основном. Но когда я сканирую, чтобы найти устройство BLE (с помощью startScan ( leScanCallback )), оно никогда не переходит в метод обратного leScanCallback вызова . Я следовал некоторым инструкциям, но не могу понять, почему это не работает.

Вот моя основная активность.kt

 package com.example.start


class MainActivity : AppCompatActivity() {

    private lateinit var drawerLayout: DrawerLayout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        drawerLayout = binding.drawerLayout
        val navController = this.findNavController(R.id.myNavHostFragment)
        NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)   //link the navigation controller amp; drawer layout to the app bar
        NavigationUI.setupWithNavController(binding.navView, navController) //allows the user to display the navigation drawer

        //PERMISSION
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            if (ContextCompat.checkSelfPermission(baseContext,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
                val PERMISSION_CODE = 0 //Should be >= 0
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                    PERMISSION_CODE)
            }
        }

        //==========================Bluetooth Part==========================================
        val REQUEST_ENABLE_BT : Int = 1 //Will stock the result of enabling the bluetooth
                                        //RESULT_OK =  -1 (0xffffffff)
                                        //RESULT_CANCELLED = 0 (0x00000000)
        //val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
        val bluetoothAdapter: BluetoothAdapter by lazy {
            val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
            bluetoothManager.adapter
        }
        if (bluetoothAdapter == null) {
            // Device doesn't support Bluetooth
        }
        if (bluetoothAdapter?.isEnabled == false) { //If bluetooth is disable, we active it
            val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
        }
    }
}
 

А вот мой файл ConnectFragment.kt, где я нажимаю кнопку «btnScan», чтобы начать исследование

 package com.example.start

private const val SELECT_DEVICE_REQUEST_CODE = 0

class ConnectFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        //return inflater.inflate(R.layout.fragment_connect, container, false)
        val binding = DataBindingUtil.inflate<FragmentConnectBinding>(
                inflater, R.layout.fragment_connect, container, false
        )
        binding.btnScan.setOnClickListener {
            view : View ->
            scanLeDevice()
        }
        return binding.root
    }

    private val bluetoothLeScanner: BluetoothLeScanner
        get() {
            val bluetoothManager = requireContext().getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
            val bluetoothAdapter = bluetoothManager.adapter
            return bluetoothAdapter.bluetoothLeScanner
        }
    // Device scan callback.
    private val leScanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            super.onScanResult(callbackType, result)
            //TODO TEST TEST TEST
            Log.d("ScanDeviceActivity", "leScanCallback >>")
            Log.d("ScanDeviceActivity", "onScanResult(): ${result?.device?.address} - ${result?.device?.name}")
        }
        override fun onBatchScanResults(results: MutableList<ScanResult>?) {
            super.onBatchScanResults(results)
            Log.d("DeviceListActivity","onBatchScanResults:${results.toString()}")
        }

        override fun onScanFailed(errorCode: Int) {
            super.onScanFailed(errorCode)
            Log.d("DeviceListActivity", "onScanFailed: $errorCode")
        }
    }
    private var scanning = false
    private val handler = Handler()
    // Stops scanning after 10 seconds.
    private val SCAN_PERIOD: Long = 10000

    fun scanLeDevice() {
        if (!scanning) { // Stops scanning after a pre-defined scan period.
            handler.postDelayed({
                scanning = false
                bluetoothLeScanner.stopScan(leScanCallback)
            }, SCAN_PERIOD)
            scanning = true
            //PERMISSION COARSE LOCATION
            Log.d("ScanDeviceStart", "startScan()")
            when (PermissionChecker.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION)) {
                PackageManager.PERMISSION_GRANTED -> bluetoothLeScanner.startScan(leScanCallback)
                else -> requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 1)
            }
        } else {
            scanning = false
            bluetoothLeScanner.stopScan(leScanCallback)
        }
    }

    //Permission
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        when (requestCode) {
            1 -> when (grantResults) {
                intArrayOf(PackageManager.PERMISSION_GRANTED) -> {
                    Log.d("ScanDevices", "onRequestPermissionsResult(PERMISSION_GRANTED)")
                    bluetoothLeScanner.startScan(leScanCallback)
                }
                else -> {
                    Log.d("ScanDevices", "onRequestPermissionsResult(not PERMISSION_GRANTED)")
                }
            }
            else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        }
    }
}
 

После того, как я нажал свою кнопку, консоль отладки заканчивается:
D/Bluetooth-сканер: Остановите сканирование с помощью обратного вызова

Комментарии:

1. Я предлагаю проверить, работает ли этот пример на вашем устройстве, и изменить его в соответствии с вашими целями: проект BLEProof на github — 2 приложения, центральные и периферийные, весь код в MainActivity.kt

2. Мне удалось заставить его работать с вашим решением. Я полностью заменил свой код тем, который вы дали. Спасибо

3. Сыворотку вы используете Handler вместо Coroutines ?