| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- package com.narutohuo.xindazhou.ble.util
- import android.bluetooth.BluetoothAdapter
- import android.bluetooth.BluetoothDevice
- import android.bluetooth.BluetoothManager
- import android.bluetooth.le.BluetoothLeScanner
- import android.bluetooth.le.ScanCallback
- import android.bluetooth.le.ScanFilter
- import android.bluetooth.le.ScanResult
- import android.bluetooth.le.ScanSettings
- import android.content.Context
- import android.os.Handler
- import android.os.Looper
- import com.narutohuo.xindazhou.core.log.ILog
- /**
- * BLE扫描器
- *
- * 负责 BLE 设备的扫描,返回符合过滤条件的 BluetoothDevice 列表
- * 使用 Android 12+ 推荐的 BluetoothLeScanner API
- */
- class BleScanner(
- private val context: Context,
- private val onDeviceFound: (BluetoothDevice) -> Unit,
- private val onScanFinished: () -> Unit,
- private val filterDeviceName: String? = null,
- private val filterDeviceAddress: String? = null
- ) {
-
- private val tag = "BleScanner"
-
- private val bluetoothAdapter: BluetoothAdapter by lazy {
- val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
- manager.adapter
- }
-
- private var scanner: BluetoothLeScanner? = null
- private var scanning = false
-
- private val scanCallback = object : ScanCallback() {
- override fun onScanResult(callbackType: Int, result: ScanResult) {
- val device = result.device
- if (device != null) {
- ILog.d(tag, "========== 发现设备 ==========")
- ILog.d(tag, "设备名称: ${device.name ?: "未知"}")
- ILog.d(tag, "设备地址: ${device.address}")
- ILog.d(tag, "RSSI: ${result.rssi}")
- ILog.d(tag, "扫描类型: $callbackType (1=CALLBACK_TYPE_ALL_MATCHES, 2=BATCH)")
- ILog.d(tag, "====================================")
-
- // 设备过滤(OR逻辑:名称匹配或地址匹配)
- val nameMatches = filterDeviceName?.let {
- device.name?.equals(it, ignoreCase = true) == true
- } ?: false
-
- val addressMatches = filterDeviceAddress?.let {
- device.address.equals(it, ignoreCase = true)
- } ?: false
-
- val shouldConnect = if (filterDeviceName != null || filterDeviceAddress != null) {
- // 如果设置了过滤条件,则必须匹配(OR逻辑)
- nameMatches || addressMatches
- } else {
- // 如果没有设置过滤条件,则连接所有设备
- true
- }
-
- if (shouldConnect) {
- ILog.d(tag, "✅ 设备匹配过滤条件(名称匹配=$nameMatches, 地址匹配=$addressMatches),准备连接")
- onDeviceFound(device)
- stopScan()
- } else {
- ILog.d(tag, "⏭️ 设备不匹配过滤条件,跳过")
- }
- }
- }
-
- override fun onBatchScanResults(results: List<ScanResult>) {
- ILog.d(tag, "批量扫描结果: ${results.size} 个设备")
- results.forEach {
- it.device?.let { device ->
- ILog.d(tag, "发现设备: ${device.name ?: "未知"}, 地址: ${device.address}, RSSI: ${it.rssi}")
- onDeviceFound(device)
- stopScan()
- }
- }
- }
-
- override fun onScanFailed(errorCode: Int) {
- ILog.e(tag, "========== 扫描失败 ==========")
- ILog.e(tag, "错误码: $errorCode")
- ILog.e(tag, "1=SCAN_FAILED_ALREADY_STARTED, 2=SCAN_FAILED_APPLICATION_REGISTRATION_FAILED, 3=SCAN_FAILED_INTERNAL_ERROR, 4=SCAN_FAILED_FEATURE_UNSUPPORTED, 5=SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES")
- ILog.e(tag, "====================================")
- stopScan()
- }
- }
-
- /**
- * 开始扫描(默认 10 秒后自动停止)
- */
- @android.annotation.SuppressLint("MissingPermission")
- fun startScan(filterNamePrefix: String? = null, timeoutMs: Long = 10_000L) {
- ILog.d(tag, "========== 开始扫描 ==========")
- ILog.d(tag, "过滤前缀: ${filterNamePrefix ?: "无"}")
- ILog.d(tag, "超时时间: ${timeoutMs}ms")
- ILog.d(tag, "蓝牙适配器状态: ${if (bluetoothAdapter.isEnabled) "已开启" else "已关闭"}")
- ILog.d(tag, "====================================")
-
- if (scanning) {
- ILog.w(tag, "⚠️ 扫描已在进行中,忽略重复请求")
- return
- }
-
- if (!bluetoothAdapter.isEnabled) {
- ILog.e(tag, "❌ 蓝牙未开启,无法扫描")
- return
- }
-
- scanner = bluetoothAdapter.bluetoothLeScanner
- if (scanner == null) {
- ILog.e(tag, "❌ BluetoothLeScanner 为 null")
- return
- }
-
- val settings = ScanSettings.Builder()
- .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
- .build()
-
- val filters = mutableListOf<ScanFilter>()
- filterNamePrefix?.let {
- filters.add(ScanFilter.Builder().setDeviceName(it).build())
- ILog.d(tag, "已设置设备名称过滤: $it")
- }
-
- try {
- scanner?.startScan(filters, settings, scanCallback)
- scanning = true
- ILog.d(tag, "✅ 扫描已启动,等待设备发现...")
-
- // 超时自动停止
- Handler(Looper.getMainLooper()).postDelayed({
- if (scanning) {
- ILog.w(tag, "⏰ 扫描超时 (${timeoutMs}ms),自动停止")
- stopScan()
- }
- }, timeoutMs)
- } catch (e: SecurityException) {
- ILog.e(tag, "❌ 扫描权限不足", e)
- scanning = false
- } catch (e: Exception) {
- ILog.e(tag, "❌ 启动扫描失败", e)
- scanning = false
- }
- }
-
- /**
- * 停止扫描
- */
- @android.annotation.SuppressLint("MissingPermission")
- fun stopScan() {
- if (!scanning) {
- ILog.d(tag, "stopScan() 被调用,但未在扫描中")
- return
- }
-
- ILog.d(tag, "停止扫描...")
-
- try {
- scanner?.stopScan(scanCallback)
- scanning = false
- ILog.d(tag, "停止扫描")
- onScanFinished()
- } catch (e: SecurityException) {
- ILog.e(tag, "停止扫描权限不足", e)
- } catch (e: Exception) {
- ILog.e(tag, "停止扫描失败", e)
- }
- }
- }
|