Android开发 BtHelp 一个封装好的经典蓝牙功能操作类


前言

  请注意,这里说的是经典蓝牙开发。就是手机设置中的蓝牙开发(代码的使用场景是一些Launcher开发与物联设备开发),并不是ble蓝牙开发。使用kotlin与建造者模式封装,直接复制完整封装代码就可以使用(包含了数据类与枚举类),有大量的反射代码(kotlin第一次启动反射会有较大耗时,后续调用正常)。

 

使用

    override fun onResume() {
        super.onResume()
        mBtHelp.startDiscovery()
    }

    override fun onStop() {
        super.onStop()
        mBtHelp.cancelDiscovery()
    }

    override fun onDestroy() {
        super.onDestroy()
        mBtHelp.destroy()
    }

    private fun initBt() {
        mBtHelp = BtHelp.Build(this)
            .setBluetoothStatusListener {
                Log.e("zh", "蓝牙开关状态:${it} ")
                if (it == BluetoothAdapter.STATE_OFF) {
                    mAdapter.refreshPairData(null)
                    mAdapter.refreshNoPairData(null)
                    mAdapter.setEnableBluetoothStatus(mBtHelp.isEnabled())
                }
                if (it == BluetoothAdapter.STATE_ON) {
                    mFirstSearch = true
                    mAdapter.setEnableBluetoothStatus(mBtHelp.isEnabled())
                    mBtHelp.startDiscovery()
                }
            }
            .setStartDiscoveryCallback {
                Log.d("zh", "开始搜索: ")
                if (mFirstSearch) {
                    mAdapter.refreshNoPairData(null)
                }
            }
            .setDiscoveringCallback { blDevice, list ->
                val sb = StringBuilder()
                list.forEach { sb.append("${it.bluetoothDevice.name}, ") }
                Log.d("zh", "搜索中发现: ${blDevice?.bluetoothDevice?.name} blDevice=${blDevice}  list=${sb} ")
                mAdapter.addNoPairData(blDevice)
            }
            .setFinishDiscoveryCallback {
                val sb = StringBuilder()
                it.forEach { sb.append("${it.bluetoothDevice.name}, ") }
                Log.d("zh", "搜索完成: list=${sb}")
                mAdapter.refreshNoPairData(it)
                mFirstSearch = false
                mBtHelp.startDiscovery()
            }
            .setAlreadyPairedDeviceCallback {
                val sb = StringBuilder()
                it.forEach { sb.append("${it.bluetoothDevice.name}, ") }
                Log.d("zh", "已经配对设备: list=${sb}")
                mAdapter.refreshPairData(it)
            }
            .setPairStatusListener { btDeviceData, i ->
                when (i) {
                    BluetoothDevice.BOND_BONDING -> {
                        mAdapter.setPairDeviceStatus(btDeviceData to "正在配对")
                    }
                    BluetoothDevice.BOND_BONDED -> {
                        mAdapter.setPairDeviceStatus(btDeviceData to "配对成功")
                        mIsPair = false
                        mAdapter.refreshNoPairData(null)
                    }
                    BluetoothDevice.BOND_NONE -> {
                        if (mIsPair) {
                            Toast.makeText(this, "配对不成功", Toast.LENGTH_SHORT).show()
                        }
                        mIsPair = false
                    }
                }
            }
            .build()
    }

代码

所需权限

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

代码

import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothClass
import android.bluetooth.BluetoothDevice
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.reflect.Method
import kotlin.reflect.full.declaredFunctions
@SuppressLint("MissingPermission")
class BtHelp {
private val TAG = "lwlx"
private val mBlAdapter by lazy { BluetoothAdapter.getDefaultAdapter() }
private var mBtReceiver: BtReceiver? = null
private var mBuild: Build? = null
private val mPairDeviceList = mutableListOf<BtDeviceData>()
private var mHandler: Handler? = null
private fun init(build: Build) {
mBuild = build
mHandler = Handler(Looper.getMainLooper())
registeredBroadcastReceiver()
}
/**
* 销毁
*/
fun destroy() {
unregisteredBroadcastReceiver()
mHandler?.removeCallbacksAndMessages(null)
mHandler = null
mBuild?.mContext = null
mBuild?.mStartDiscoveryCallback = null
mBuild?.mDiscoveringCallback = null
mBuild?.mFinishDiscoveryCallback = null
mBuild?.mAlreadyPairedDeviceCallback = null
mBuild?.mBluetoothStatusListener = null
mBuild?.mPairStatusListener = null
mBuild = null
}
/**
* 蓝牙名称
*/
fun getBluetoothName() = mBlAdapter.name
/**
* 设置开启or关闭蓝牙
*/
fun setEnabled(isEnabled: Boolean): Boolean {
if (mBlAdapter == null) {
Log.e("lwlx", "该设备不支持蓝牙")
return false
}
if (isEnabled) {
if (!mBlAdapter.isEnabled) {
return mBlAdapter.enable()
}
} else {
if (mBlAdapter.isEnabled) {
return mBlAdapter.disable()
}
}
return false
}
/**
* 是否启用蓝牙
*/
fun isEnabled(): Boolean {
if (mBlAdapter == null) {
return false
}
return mBlAdapter.isEnabled
}
/**
* 蓝牙可见(蓝牙可以被其他设备发现) 暂时未实现
* @Hide
*/
fun btVisible() {
//开启被其它蓝牙设备发现的功能
//getScanMode 获得扫描模式                                    扫描-模式-连接-发现
if (mBlAdapter.scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
val i = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE) //行动-请求-发现
//设置为一直开启 0是一直开着,如果设置了时间就会按照设置时间显示蓝牙可见
//            i.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0)
//            startActivity(i)
}
}
/**
* 搜索发现设备
*/
fun startDiscovery() {
if (mBlAdapter == null) {
return
}
if (!mBlAdapter.isDiscovering) {
mBlAdapter.startDiscovery()
}
}
/**
* 取消搜索发现设备
*/
fun cancelDiscovery() {
if (mBlAdapter == null) {
return
}
if (mBlAdapter.isDiscovering) {
mBlAdapter.cancelDiscovery()
}
}
/**
* 配对(配对成功与失败通过广播返回)
*
* @param device
*/
fun pairDevice(device: BluetoothDevice?) {
if (device == null) {
Log.e(TAG, "bond device null")
return
}
if (!mBlAdapter.isEnabled) {
Log.e(TAG, "Bluetooth not enable!")
return
}
//配对之前把扫描关闭
if (mBlAdapter.isDiscovering()) {
mBlAdapter.cancelDiscovery()
}
GlobalScope.launch(Dispatchers.IO) {
//判断设备是否配对,没有配对在配,配对了就不需要配了
if (device.bondState == BluetoothDevice.BOND_NONE) {
try {
val createBondMethod: Method = device.javaClass.getMethod("createBond")
createBondMethod.invoke(device)
} catch (e: Exception) {
e.printStackTrace()
Log.e(TAG, "attemp to bond fail!")
}
}
}
}
/**
* 取消配对(取消配对成功与失败通过广播返回 也就是配对失败)
*
* @param device
*/
fun removeDevice(device: BluetoothDevice?) {
if (device == null) {
Log.d(TAG, "cancel bond device null")
return
}
if (!mBlAdapter.isEnabled) {
Log.e(TAG, "Bluetooth not enable!")
return
}
val index = mPairDeviceList.indexOf(mPairDeviceList.find { it.bluetoothDevice.address == device.address })
mPairDeviceList.removeAt(index)
mBuild?.mAlreadyPairedDeviceCallback?.invoke(mPairDeviceList)
GlobalScope.launch(Dispatchers.IO) {
//判断设备是否配对,没有配对就不用取消了
if (device.bondState != BluetoothDevice.BOND_NONE) {
try {
val removeBondMethod: Method = device.javaClass.getMethod("removeBond")
removeBondMethod.invoke(device)
} catch (e: Exception) {
e.printStackTrace()
Log.e(TAG, "attemp to cancel bond fail!")
}
}
}
}
/**
* 已连接的设备
*/
private fun refreshConnectedDevice() {
GlobalScope.launch(Dispatchers.IO) {
val declaredFunctions = BluetoothAdapter::class.declaredFunctions
mPairDeviceList.clear()
for (f in declaredFunctions) {
if (f.name == "getConnectionState") {
//已经绑定过的全部设备,这些设备可能没有连接
val devices: Set<BluetoothDevice> = mBlAdapter.getBondedDevices()
for (device in devices) {
val declaredFunctions = BluetoothDevice::class.declaredFunctions
for (item in declaredFunctions) {
if (item.name == "isConnected") {
val isConnected = item.call(device) as Boolean
val status = if (isConnected) BluetoothStatus.PAIR_CONNECT else BluetoothStatus.PAIR_NO_CONNECT
mPairDeviceList.add(BtDeviceData(device, BluetoothTypeUtil.getDeviceType(device.bluetoothClass), status))
}
}
}
}
}
withContext(Dispatchers.Main) {
mBuild?.mAlreadyPairedDeviceCallback?.invoke(mPairDeviceList)
}
}
}
private fun registeredBroadcastReceiver() {
mBtReceiver = BtReceiver()
val intentFilter = IntentFilter()
//蓝牙开关状态
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
//蓝牙搜索
intentFilter.addAction(BluetoothDevice.ACTION_FOUND)
//开始搜索
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
//结束搜索
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
//蓝牙设备连接
intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
//蓝牙设备断开
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
//配对广播
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
mBuild?.mContext?.registerReceiver(mBtReceiver, intentFilter)
}
private fun unregisteredBroadcastReceiver() {
mBuild?.mContext?.unregisterReceiver(mBtReceiver)
}
class Build(context: Context) {
internal var mContext: Context? = context
//开始搜索
internal var mStartDiscoveryCallback: (() -> Unit)? = null
//搜索发现中
internal var mDiscoveringCallback: ((BtDeviceData, List<BtDeviceData>) -> Unit)? = null
//完成搜索
internal var mFinishDiscoveryCallback: ((List<BtDeviceData>) -> Unit)? = null
//已经配对设备
internal var mAlreadyPairedDeviceCallback: ((List<BtDeviceData>) -> Unit)? = null
//蓝牙状态
internal var mBluetoothStatusListener: ((Int) -> Unit)? = null
//配对状态
internal var mPairStatusListener: ((BtDeviceData, Int) -> Unit)? = null
/**
* 设置开始搜索回调
*/
fun setStartDiscoveryCallback(callback: (() -> Unit)): Build {
mStartDiscoveryCallback = callback
return this
}
/**
* 设置搜索发现设备回调
*/
fun setDiscoveringCallback(callback: ((BtDeviceData, List<BtDeviceData>) -> Unit)): Build {
mDiscoveringCallback = callback
return this
}
/**
* 设置结束搜索回调
*/
fun setFinishDiscoveryCallback(callback: (List<BtDeviceData>) -> Unit): Build {
mFinishDiscoveryCallback = callback
return this
}
/**
* 设置已配对设备回调
*/
fun setAlreadyPairedDeviceCallback(callback: ((List<BtDeviceData>) -> Unit)): Build {
mAlreadyPairedDeviceCallback = callback
return this
}
/**
* 蓝牙状态监听
* [Int] 蓝牙状态
* BluetoothAdapter.STATE_TURNING_ON  //蓝牙正在开启
* BluetoothAdapter.STATE_ON //蓝牙已经开启
* BluetoothAdapter.STATE_TURNING_OFF //蓝牙正在关闭
* BluetoothAdapter.STATE_OFF //蓝牙已经关闭
* BluetoothAdapter.ERROR //异常
*/
fun setBluetoothStatusListener(listener: ((Int) -> Unit)): Build {
mBluetoothStatusListener = listener
return this
}
/**
* 设置蓝牙配对状态监听
* [BtDeviceData] 正在配对的设备
* [Int] 配对状态
* BluetoothDevice.BOND_BONDING     //正在配对
* BluetoothDevice.BOND_BONDED      //配对结束
* BluetoothDevice.BOND_NONE        //取消配对/未配对
*/
fun setPairStatusListener(listener: ((BtDeviceData, Int) -> Unit)): Build {
mPairStatusListener = listener
return this
}
fun build(): BtHelp {
val btHelp = BtHelp()
btHelp.init(this)
return btHelp
}
}
inner class BtReceiver : BroadcastReceiver() {
private val noPairList = mutableListOf<BtDeviceData>()
override fun onReceive(context: Context?, intent: Intent) {
when (intent.action) {
BluetoothAdapter.ACTION_STATE_CHANGED -> {
val blState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0)
mBuild?.mBluetoothStatusListener?.invoke(blState)
}
BluetoothDevice.ACTION_ACL_CONNECTED -> {
val bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) as BluetoothDevice
}
BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
val bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) as BluetoothDevice
mHandler?.postDelayed(Runnable {
refreshConnectedDevice()
}, 1000)
}
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
val device: BluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
val blDevice = BtDeviceData(device, BluetoothTypeUtil.getDeviceType(device.bluetoothClass), BluetoothStatus.NO_PAIR)
mBuild?.mPairStatusListener?.invoke(blDevice, device.bondState)
when (device.bondState) {
BluetoothDevice.BOND_BONDING ->  {} //正在配对
BluetoothDevice.BOND_BONDED -> {
//配对结束
startDiscovery()
}
BluetoothDevice.BOND_NONE -> {
//取消配对/未配对
startDiscovery()
}
}
}
//发现设备
BluetoothDevice.ACTION_FOUND -> {
val bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) as BluetoothDevice
//设备类型
BluetoothTypeUtil.getDeviceType(bluetoothDevice.bluetoothClass)
val blDevice = BtDeviceData(bluetoothDevice, BluetoothTypeUtil.getDeviceType(bluetoothDevice.bluetoothClass), BluetoothStatus.NO_PAIR)
//去重未配对的列表
val isRepeat = noPairList.contains(blDevice)
//排除已经配对的设备
val paired = mPairDeviceList.find { bluetoothDevice.address == it.bluetoothDevice.address }
if (!blDevice.bluetoothDevice.name.isNullOrEmpty() && !isRepeat && paired == null) {
noPairList.add(blDevice)
mBuild?.mDiscoveringCallback?.invoke(blDevice, noPairList)
}
}
//开始搜索
BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
noPairList.clear()
mBuild?.mStartDiscoveryCallback?.invoke()
refreshConnectedDevice()
}
//搜索完毕
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
mBuild?.mFinishDiscoveryCallback?.invoke(noPairList)
}
}
}
}
}
data class BtDeviceData(val bluetoothDevice: BluetoothDevice, val bluetoothType: BluetoothType, val status: BluetoothStatus)
/**
* 设备类型
*/
enum class BluetoothType {
/**
* 电脑
*/
COMPUTER,
/**
* 手机
*/
PHONE,
/**
* 外围设备(键盘,鼠标,遥控器)
*/
PERIPHERAL,
/**
* 摄像头
*/
IMAGING,
/**
* 耳机
*/
HEADPHONES,
/**
* 未知
*/
UNKNOWN
}
enum class BluetoothStatus {
/**
* 配对且连接
*/
PAIR_CONNECT
/**
* 配对未连接
*/
,
PAIR_NO_CONNECT,
/**
* 没有配对
*/
NO_PAIR
}
private object BluetoothTypeUtil {
const val PROFILE_HEADSET = 0
const val PROFILE_A2DP = 1
const val PROFILE_OPP = 2
const val PROFILE_HID = 3
const val PROFILE_PANU = 4
const val PROFILE_NAP = 5
const val PROFILE_A2DP_SINK = 6
fun getDeviceType(bluetoothClass: BluetoothClass?): BluetoothType {
return if (bluetoothClass == null) {
BluetoothType.UNKNOWN
} else if (doesClassMatch(bluetoothClass, PROFILE_HEADSET) || doesClassMatch(bluetoothClass, PROFILE_A2DP)) {
BluetoothType.HEADPHONES
} else when (bluetoothClass.majorDeviceClass) {
BluetoothClass.Device.Major.COMPUTER -> BluetoothType.COMPUTER
BluetoothClass.Device.Major.PHONE -> BluetoothType.PHONE
BluetoothClass.Device.Major.PERIPHERAL -> BluetoothType.PERIPHERAL
BluetoothClass.Device.Major.IMAGING -> BluetoothType.IMAGING
else ->
BluetoothType.UNKNOWN
}
}
fun doesClassMatch(bluetoothClass: BluetoothClass, profile: Int): Boolean {
return if (profile == PROFILE_A2DP) {
if (bluetoothClass.hasService(BluetoothClass.Service.RENDER)) {
return true
}
when (bluetoothClass.deviceClass) {
BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO, BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES, BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER, BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO -> true
else -> false
}
} else if (profile == PROFILE_A2DP_SINK) {
if (bluetoothClass.hasService(BluetoothClass.Service.CAPTURE)) {
return true
}
when (bluetoothClass.deviceClass) {
BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO, BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX, BluetoothClass.Device.AUDIO_VIDEO_VCR -> true
else -> false
}
} else if (profile == PROFILE_HEADSET) {
// The render service class is required by the spec for HFP, so is a
// pretty good signal
if (bluetoothClass.hasService(BluetoothClass.Service.RENDER)) {
return true
}
when (bluetoothClass.deviceClass) {
BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE, BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET, BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO -> true
else -> false
}
} else if (profile == PROFILE_OPP) {
if (bluetoothClass.hasService(BluetoothClass.Service.OBJECT_TRANSFER)) {
return true
}
when (bluetoothClass.deviceClass) {
BluetoothClass.Device.COMPUTER_UNCATEGORIZED, BluetoothClass.Device.COMPUTER_DESKTOP, BluetoothClass.Device.COMPUTER_SERVER, BluetoothClass.Device.COMPUTER_LAPTOP, BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA, BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA, BluetoothClass.Device.COMPUTER_WEARABLE, BluetoothClass.Device.PHONE_UNCATEGORIZED, BluetoothClass.Device.PHONE_CELLULAR, BluetoothClass.Device.PHONE_CORDLESS, BluetoothClass.Device.PHONE_SMART, BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY, BluetoothClass.Device.PHONE_ISDN -> true
else -> false
}
} else if (profile == PROFILE_HID) {
bluetoothClass.deviceClass and BluetoothClass.Device.Major.PERIPHERAL == BluetoothClass.Device.Major.PERIPHERAL
} else if (profile == PROFILE_PANU || profile == PROFILE_NAP) {
// No good way to distinguish between the two, based on class bits.
if (bluetoothClass.hasService(BluetoothClass.Service.NETWORKING)) {
true
} else bluetoothClass.deviceClass and BluetoothClass.Device.Major.NETWORKING == BluetoothClass.Device.Major.NETWORKING
} else {
false
}
}
}

 

 

 

 

End

原创文章,作者:kepupublish,如若转载,请注明出处:https://blog.ytso.com/272965.html

(0)
上一篇 2022年7月9日
下一篇 2022年7月9日

相关推荐

发表回复

登录后才能评论