其他分享
首页 > 其他分享> > Android开发 BtHelp 一个封装好的经典蓝牙功能操作类

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

标签:BluetoothDevice,BtHelp,val,BluetoothClass,蓝牙,fun,Device,Android,null
来源: https://www.cnblogs.com/guanxinjing/p/16355400.html