From 2e58b77dcc5c46ee9880c6199042433f3c35f660 Mon Sep 17 00:00:00 2001 From: yimiao Date: Thu, 9 Dec 2021 16:02:39 +0800 Subject: [PATCH] =?UTF-8?q?[desc]:=E5=90=8C=E6=AD=A5=E8=93=9D=E7=89=99?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=BB=A3=E7=A0=81=20[author]:wangyimiao?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- commonbt/src/main/AndroidManifest.xml | 16 +- .../java/com/common/bluetooth/BtConstants.kt | 7 + .../com/common/bluetooth/BtDemoActivity.java | 16 +- .../java/com/common/bluetooth/BtManager.kt | 284 +++++++--- .../bluetooth/callback/BtMsgListener.kt | 33 ++ .../bluetooth/callback/BtServiceListener.kt | 19 + ...iverListener.kt => MsgReceiverListener.kt} | 2 +- .../bluetooth/interfaces/IBluetoothClient.kt | 4 +- .../bluetooth/service/BluetoothCommon.kt | 120 ++++ .../service/BluetoothConnectService.java | 519 ------------------ .../bluetooth/service/BluetoothLeClient.java | 172 ------ .../service/{ => ble}/BLEClientService.java | 6 +- .../service/{ => ble}/BLEReceiveService.java | 11 +- .../ble}/BluetoothClientBLEAdapter.java | 6 +- .../service/ble/BluetoothLeClient.java | 79 +++ .../{ => ble}/BluetoothLeConnector.java | 7 +- .../{ => ble}/BluetoothLeSearcher.java | 2 +- .../service/bt/BTCClientService.java | 102 ++++ .../service/bt/BTCReceiverService.java | 108 ++++ .../service/bt/BluetoothClassicBase.java | 259 +++++++++ .../service/bt/BluetoothClassicClient.java | 56 ++ .../{ => bt}/BluetoothClassicSearcher.java | 2 +- .../service/bt/BluetoothClassicServer.java | 59 ++ .../bt/BluetoothClientClassicAdapter.kt | 98 ++++ .../bluetooth/service/bt/BtReceiver.java | 94 ++++ .../common/bluetooth/{ => utils}/BtUtils.kt | 7 +- .../bluetooth/view/BtDeviceListAdapter.java | 4 +- .../src/main/res/layout/activity_main.xml | 13 + 28 files changed, 1319 insertions(+), 786 deletions(-) create mode 100644 commonbt/src/main/java/com/common/bluetooth/callback/BtMsgListener.kt create mode 100644 commonbt/src/main/java/com/common/bluetooth/callback/BtServiceListener.kt rename commonbt/src/main/java/com/common/bluetooth/callback/{BleMsgReceiverListener.kt => MsgReceiverListener.kt} (84%) create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/BluetoothCommon.kt delete mode 100644 commonbt/src/main/java/com/common/bluetooth/service/BluetoothConnectService.java delete mode 100644 commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeClient.java rename commonbt/src/main/java/com/common/bluetooth/service/{ => ble}/BLEClientService.java (98%) rename commonbt/src/main/java/com/common/bluetooth/service/{ => ble}/BLEReceiveService.java (97%) rename commonbt/src/main/java/com/common/bluetooth/{adapter => service/ble}/BluetoothClientBLEAdapter.java (97%) create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeClient.java rename commonbt/src/main/java/com/common/bluetooth/service/{ => ble}/BluetoothLeConnector.java (99%) rename commonbt/src/main/java/com/common/bluetooth/service/{ => ble}/BluetoothLeSearcher.java (99%) create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/bt/BTCClientService.java create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/bt/BTCReceiverService.java create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicBase.java create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicClient.java rename commonbt/src/main/java/com/common/bluetooth/service/{ => bt}/BluetoothClassicSearcher.java (96%) create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicServer.java create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClientClassicAdapter.kt create mode 100644 commonbt/src/main/java/com/common/bluetooth/service/bt/BtReceiver.java rename commonbt/src/main/java/com/common/bluetooth/{ => utils}/BtUtils.kt (99%) diff --git a/commonbt/src/main/AndroidManifest.xml b/commonbt/src/main/AndroidManifest.xml index 7b80fd7..3ff51be 100644 --- a/commonbt/src/main/AndroidManifest.xml +++ b/commonbt/src/main/AndroidManifest.xml @@ -25,13 +25,25 @@ android:screenOrientation="portrait" android:theme="@style/Theme.AppCompat"/> + + + + + + diff --git a/commonbt/src/main/java/com/common/bluetooth/BtConstants.kt b/commonbt/src/main/java/com/common/bluetooth/BtConstants.kt index ea514ae..a33e5d0 100644 --- a/commonbt/src/main/java/com/common/bluetooth/BtConstants.kt +++ b/commonbt/src/main/java/com/common/bluetooth/BtConstants.kt @@ -2,6 +2,7 @@ package com.common.bluetooth import android.content.Context import android.provider.Settings +import java.nio.charset.StandardCharsets import java.util.* /** @@ -13,6 +14,8 @@ import java.util.* object BtConstants { const val BT_NAME = "innovation bt" + val DEFAULT_CHARSET = StandardCharsets.UTF_8 + /** * 蓝牙类型 */ @@ -80,6 +83,10 @@ object BtConstants { */ const val MAX_MTU = 35 + /** + * 经典蓝牙连接UUID + */ + val CLASSIC_BT_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") val UUID_SERVICE = UUID.fromString("66f564dc-121f-3e7f-80b1-f005d3f194c9") val UUID_CHARACTERISTIC_NOTIFY = UUID.fromString("66f564dd-121f-3e7f-80b1-f005d3f194c9") val UUID_CHARACTERISTIC_WRITE = UUID.fromString("66f564de-121f-3e7f-80b1-f005d3f194c9") diff --git a/commonbt/src/main/java/com/common/bluetooth/BtDemoActivity.java b/commonbt/src/main/java/com/common/bluetooth/BtDemoActivity.java index 6310fa0..a8f1d75 100644 --- a/commonbt/src/main/java/com/common/bluetooth/BtDemoActivity.java +++ b/commonbt/src/main/java/com/common/bluetooth/BtDemoActivity.java @@ -22,6 +22,7 @@ import com.common.bluetooth.bean.CommonMsg; import com.common.bluetooth.bean.KeyboardEvent; import com.common.bluetooth.callback.BLEClientListener; import com.common.bluetooth.databinding.ActivityMainBinding; +import com.common.bluetooth.utils.BtUtils; import com.common.bluetooth.view.BtDeviceListAdapter; import com.google.gson.Gson; @@ -131,10 +132,23 @@ public class BtDemoActivity extends AppCompatActivity { } })); }); + + mBinding.btcServerBuild.setOnClickListener(v -> { + BtManager.INSTANCE.initServer(this, BtConstants.BLUETOOTH_TYPE.CLASSIC); + BtManager.INSTANCE.setMsgReceiverListener(msg -> { + Log.d(TAG, new String(msg)); + runOnUiThread(new Runnable() { + @Override + public void run() { + mBinding.btcReceiveContent.setText(new String(msg)); + } + }); + }); + }); } private void checkBluetooth() { - BtManager.INSTANCE.initClient(this, BtConstants.BLUETOOTH_TYPE.BLE); + BtManager.INSTANCE.initClient(this, BtConstants.BLUETOOTH_TYPE.CLASSIC); BtManager.INSTANCE.btPermissionCheck(this, PERMISSION_REQUEST_LOCATION); } diff --git a/commonbt/src/main/java/com/common/bluetooth/BtManager.kt b/commonbt/src/main/java/com/common/bluetooth/BtManager.kt index af1e96e..6fdf0f7 100644 --- a/commonbt/src/main/java/com/common/bluetooth/BtManager.kt +++ b/commonbt/src/main/java/com/common/bluetooth/BtManager.kt @@ -11,20 +11,25 @@ import android.content.ServiceConnection import android.content.pm.PackageManager import android.os.Build import android.os.IBinder -import android.util.Log.d -import android.util.Log.e +import android.util.Log.* import androidx.core.content.ContextCompat.checkSelfPermission import com.common.bluetooth.BtConstants.BLUETOOTH_TYPE -import com.common.bluetooth.adapter.BluetoothClientBLEAdapter +import com.common.bluetooth.service.ble.BluetoothClientBLEAdapter import com.common.bluetooth.bean.CommonMsg import com.common.bluetooth.callback.BLEClientListener -import com.common.bluetooth.callback.BleMsgReceiverListener +import com.common.bluetooth.callback.BtMsgListener +import com.common.bluetooth.callback.BtServiceListener +import com.common.bluetooth.callback.MsgReceiverListener import com.common.bluetooth.interfaces.IBluetoothClient -import com.common.bluetooth.service.BLEClientService -import com.common.bluetooth.service.BLEClientService.BLEClientBinder -import com.common.bluetooth.service.BLEReceiveService -import com.common.bluetooth.service.BLEReceiveService.ReceiverBinder -import com.common.bluetooth.service.BluetoothLeClient +import com.common.bluetooth.service.ble.BLEClientService +import com.common.bluetooth.service.ble.BLEClientService.BLEClientBinder +import com.common.bluetooth.service.ble.BLEReceiveService +import com.common.bluetooth.service.ble.BLEReceiveService.ReceiverBinder +import com.common.bluetooth.service.ble.BluetoothLeClient +import com.common.bluetooth.service.bt.BTCClientService +import com.common.bluetooth.service.bt.BTCReceiverService +import com.common.bluetooth.service.bt.BluetoothClassicClient +import com.common.bluetooth.service.bt.BluetoothClientClassicAdapter import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observer import io.reactivex.rxjava3.disposables.Disposable @@ -45,34 +50,59 @@ object BtManager { private var mBtClient: IBluetoothClient? = null /** - * 客户端服务 + * BLE客户端服务 */ private var clientService: BLEClientService? = null /** - * 服务端服务 + * 经典蓝牙客户端服务 + */ + private var classicClientService: BTCClientService? = null + + /** + * BLE服务端服务 */ private var serverService: BLEReceiveService? = null + /** + * 经典蓝牙服务端服务 + */ + private var classicServerService: BTCReceiverService? = null + /** * 消息接收监听 */ - var msgReceiverListener: BleMsgReceiverListener? = null + var msgReceiverListener: MsgReceiverListener? = null /** * 蓝牙客户端监听 */ var clientListener: BLEClientListener? = null + /** + * 蓝牙服务链接监听 + */ + var btServiceListener: BtServiceListener? = null + /** * 是否绑定了服务端 */ - private var isBindServer: AtomicBoolean = AtomicBoolean(false) + private var isBindBleServer: AtomicBoolean = AtomicBoolean(false) /** * 是否绑定了客户端 */ - private var isBindClient: AtomicBoolean = AtomicBoolean(false) + private var isBindBleClient: AtomicBoolean = AtomicBoolean(false) + + /** + * 是否绑定了服务端 + */ + private var isBindClassicServer: AtomicBoolean = AtomicBoolean(false) + + /** + * 是否绑定了客户端 + */ + private var isBindClassicClient: AtomicBoolean = AtomicBoolean(false) /** * 当前使用的编码 默认是UTF-8 @@ -89,77 +119,180 @@ object BtManager { * 初始化蓝牙服务端模块 */ fun initServer(context: Context, btType: BLUETOOTH_TYPE) { - mBtClient = BluetoothClientBLEAdapter(BluetoothLeClient.getInstance(context)) + if (btType == BLUETOOTH_TYPE.BLE) { + mBtClient = BluetoothClientBLEAdapter( + BluetoothLeClient.getInstance(context) + ) + initBleReceiverService(context) + } else if (btType == BLUETOOTH_TYPE.CLASSIC) { + mBtClient = BluetoothClientClassicAdapter(BluetoothClassicClient(context)) + initClassicReceiverService(context) + } mBtClient!!.checkBluetoothDevice(btType) - initReceiverService(context) + } /** * 初始化蓝牙客户端模块 */ fun initClient(context: Activity, btType: BLUETOOTH_TYPE) { - mBtClient = BluetoothClientBLEAdapter(BluetoothLeClient.getInstance(context)) + if (btType == BLUETOOTH_TYPE.BLE) { + mBtClient = BluetoothClientBLEAdapter( + BluetoothLeClient.getInstance(context) + ) + initBleClientService(context) + } else if (btType == BLUETOOTH_TYPE.CLASSIC) { + mBtClient = BluetoothClientClassicAdapter( + BluetoothClassicClient(context) + ) + initClassicClientService(context) + } mBtClient!!.checkBluetoothDevice(btType) - initClientService(context) } - /** * 初始化接收端服务 */ - private fun initReceiverService(context: Context) { + private fun initBleReceiverService(context: Context) { val intent = Intent(context, BLEReceiveService::class.java) // 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行 - context.bindService(intent, receiverConn, Context.BIND_AUTO_CREATE) - isBindServer.set(true) + context.bindService(intent, bleReceiverConn, Context.BIND_AUTO_CREATE) + isBindBleServer.set(true) + } + + private fun initClassicReceiverService(context: Context) { + val intent = Intent(context, BTCReceiverService::class.java) + // 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行 + context.bindService(intent, classicReceiverConn, Context.BIND_AUTO_CREATE) + isBindClassicServer.set(true) } /** * 初始化客户端服务 */ - private fun initClientService(context: Context) { + private fun initBleClientService(context: Context) { val intent = Intent(context, BLEClientService::class.java) // 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行 - context.bindService(intent, clientConn, Context.BIND_AUTO_CREATE) - isBindClient.set(true) + context.bindService(intent, bleClientConn, Context.BIND_AUTO_CREATE) + isBindBleClient.set(true) } - private val receiverConn: ServiceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName, service: IBinder) { - serverService = (service as ReceiverBinder).service - serverService?.setMsgReceiveListener(object : BleMsgReceiverListener { - override fun onMsgReceive(msg: ByteArray) { - msgReceiverListener?.onMsgReceive(msg) - } - }) + private fun initClassicClientService(context: Context) { + val intent = Intent(context, BTCClientService::class.java) + // 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行 + context.bindService(intent, classicClientConn, Context.BIND_AUTO_CREATE) + isBindClassicClient.set(true) + } + + private val bleReceiverConn: ServiceConnection by lazy { + object : ServiceConnection { + override fun onServiceConnected(name: ComponentName, service: IBinder) { + serverService = (service as ReceiverBinder).service + serverService?.setMsgReceiveListener(object : MsgReceiverListener { + override fun onMsgReceive(msg: ByteArray) { + msgReceiverListener?.onMsgReceive(msg) + } + }) + btServiceListener?.onServiceReady() + } + + override fun onServiceDisconnected(name: ComponentName) { + serverService = null + btServiceListener?.onServiceDisConnected() + } } + } + + private val classicReceiverConn: ServiceConnection by lazy { + object : ServiceConnection { + override fun onServiceConnected(name: ComponentName, service: IBinder) { + classicServerService = (service as BTCReceiverService.ClassicReceiverBinder).service + classicServerService?.setMsgReceiveListener(object : BtMsgListener() { + override fun socketNotify(state: Int, msg: String) { + msgReceiverListener?.onMsgReceive(msg.toByteArray()) + } + }) + btServiceListener?.onServiceReady() + } - override fun onServiceDisconnected(name: ComponentName) {} + override fun onServiceDisconnected(name: ComponentName) { + classicServerService = null + btServiceListener?.onServiceDisConnected() + } + } } - private val clientConn: ServiceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName, service: IBinder) { - clientService = (service as BLEClientBinder).service - clientService?.setClientListener(object : BLEClientListener { - override fun onResult(result: CommonMsg) { - // 如果连接成功,更新连接的设备 - if (result.msgType == BtConstants.CONNECT_SUCCESS) { - curConnectMac = result.msg - } else if (result.msgType == BtConstants.DISCONNECT) { - curConnectMac = "" + private val bleClientConn: ServiceConnection by lazy { + object : ServiceConnection { + override fun onServiceConnected(name: ComponentName, service: IBinder) { + clientService = (service as BLEClientBinder).service + clientService?.setClientListener(object : BLEClientListener { + override fun onResult(result: CommonMsg) { + // 如果连接成功,更新连接的设备 + if (result.msgType == BtConstants.CONNECT_SUCCESS) { + curConnectMac = result.msg + } else if (result.msgType == BtConstants.DISCONNECT) { + curConnectMac = "" + } + clientListener?.onResult(result) } - clientListener?.onResult(result) - } - override fun onNotifyMsgReceive(msg: ByteArray) { - clientListener?.onNotifyMsgReceive(msg) - } - }) + override fun onNotifyMsgReceive(msg: ByteArray) { + clientListener?.onNotifyMsgReceive(msg) + } + }) + btServiceListener?.onServiceReady() + } + + override fun onServiceDisconnected(name: ComponentName) { + clientService = null + clientListener?.onResult( + CommonMsg( + BtConstants.DISCONNECT, + "disconnect from server" + ) + ) + btServiceListener?.onServiceDisConnected() + } } + } + + private val classicClientConn: ServiceConnection by lazy { + object : ServiceConnection { + override fun onServiceConnected(name: ComponentName, service: IBinder) { + classicClientService = (service as BTCClientService.ClassicClientBinder).service + classicClientService?.setMsgReceiveListener(object : BtMsgListener() { + override fun socketNotify(state: Int, msg: String) { + when (state) { + CONNECTED -> { + curConnectMac = msg + clientListener?.onResult( + CommonMsg( + BtConstants.CONNECT_SUCCESS, + msg + ) + ) + } + DISCONNECTED -> { + curConnectMac = "" + clientListener?.onResult(CommonMsg(BtConstants.DISCONNECT, msg)) + } + MSG -> { + clientListener?.onNotifyMsgReceive(msg.toByteArray()) + } + else -> { + e(TAG, "got error state:$state") + } + } + } + }) + btServiceListener?.onServiceReady() + } - override fun onServiceDisconnected(name: ComponentName) { - clientService = null - clientListener?.onResult(CommonMsg(BtConstants.DISCONNECT, "disconnect from server")) + override fun onServiceDisconnected(name: ComponentName) { + classicClientService = null + btServiceListener?.onServiceDisConnected() + } } } @@ -205,10 +338,16 @@ object BtManager { * @param mac MAC地址 */ fun connect(mac: String) { - if (clientService == null) { - e(TAG, "writeMsg pls init first") - } else { + if (clientService != null) { clientService!!.connect(mac, true) + } else { + w(TAG, "init clientService first") + } + + if (classicClientService != null) { + classicClientService!!.connect(mac) + } else { + w(TAG, "init classicClientService first") } } @@ -241,11 +380,17 @@ object BtManager { * @param msg 传输的内容 */ fun write(msg: ByteArray) { - if (clientService == null) { - e(TAG, "writeMsg pls init first") - } else { + if (clientService != null) { clientService!!.write(msg) } + + if (classicClientService != null) { + classicClientService!!.write(msg) + } + + if (classicServerService != null) { + classicServerService!!.write(msg) + } } /** @@ -301,19 +446,28 @@ object BtManager { */ fun sendNotify(msg: ByteArray) { serverService?.sendNotify(msg) + classicServerService?.write(msg) } /** * 释放链接 */ fun release(context: Context?) { - if (isBindServer.get()) { - context?.unbindService(receiverConn) - isBindServer.set(false) + if (isBindBleServer.get()) { + context?.unbindService(bleReceiverConn) + isBindBleServer.set(false) + } + if (isBindClassicServer.get()) { + context?.unbindService(classicReceiverConn) + isBindClassicServer.set(false) + } + if (isBindBleClient.get()) { + context?.unbindService(bleClientConn) + isBindBleClient.set(false) } - if (isBindClient.get()) { - context?.unbindService(clientConn) - isBindClient.set(false) + if (isBindClassicClient.get()) { + context?.unbindService(classicClientConn) + isBindClassicServer.set(false) } } } \ No newline at end of file diff --git a/commonbt/src/main/java/com/common/bluetooth/callback/BtMsgListener.kt b/commonbt/src/main/java/com/common/bluetooth/callback/BtMsgListener.kt new file mode 100644 index 0000000..1bc35e5 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/callback/BtMsgListener.kt @@ -0,0 +1,33 @@ +package com.common.bluetooth.callback + +/** + * 经典蓝牙监听 + * + * @author wangym + * @since 2021-12-1 + */ +abstract class BtMsgListener { + /** + * 通知 + * @param state 消息类型 + * @param msg 消息内容 + */ + open fun socketNotify(state: Int, msg: String) {} + + companion object { + /** + * 断开连接 + */ + const val DISCONNECTED = 0 + + /** + * 连接成功 + */ + const val CONNECTED = 1 + + /** + * 消息 + */ + const val MSG = 2 + } +} \ No newline at end of file diff --git a/commonbt/src/main/java/com/common/bluetooth/callback/BtServiceListener.kt b/commonbt/src/main/java/com/common/bluetooth/callback/BtServiceListener.kt new file mode 100644 index 0000000..7e82915 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/callback/BtServiceListener.kt @@ -0,0 +1,19 @@ +package com.common.bluetooth.callback + +/** + * BT服务监听 + * + * @author wangym + * @since 2021-12-9 + */ +interface BtServiceListener { + /** + * 服务准备完毕 + */ + fun onServiceReady() + + /** + * 服务断开 + */ + fun onServiceDisConnected() +} \ No newline at end of file diff --git a/commonbt/src/main/java/com/common/bluetooth/callback/BleMsgReceiverListener.kt b/commonbt/src/main/java/com/common/bluetooth/callback/MsgReceiverListener.kt similarity index 84% rename from commonbt/src/main/java/com/common/bluetooth/callback/BleMsgReceiverListener.kt rename to commonbt/src/main/java/com/common/bluetooth/callback/MsgReceiverListener.kt index 5986236..bea99f4 100644 --- a/commonbt/src/main/java/com/common/bluetooth/callback/BleMsgReceiverListener.kt +++ b/commonbt/src/main/java/com/common/bluetooth/callback/MsgReceiverListener.kt @@ -3,7 +3,7 @@ package com.common.bluetooth.callback /** * BLE数据接收监听 */ -interface BleMsgReceiverListener { +interface MsgReceiverListener { /** * 接收的消息 * diff --git a/commonbt/src/main/java/com/common/bluetooth/interfaces/IBluetoothClient.kt b/commonbt/src/main/java/com/common/bluetooth/interfaces/IBluetoothClient.kt index 963d151..c0ec305 100644 --- a/commonbt/src/main/java/com/common/bluetooth/interfaces/IBluetoothClient.kt +++ b/commonbt/src/main/java/com/common/bluetooth/interfaces/IBluetoothClient.kt @@ -24,7 +24,7 @@ interface IBluetoothClient { * false 如果当前正在进行扫描操作则会抛出 [com.common.bluetooth.exception.BluetoothSearchConflictException] 错误 * @return 扫描结果的列表(无重复设备) */ - fun search(millis: Int, cancel: Boolean): Observable + fun search(millis: Long, cancel: Boolean): Observable /** * 停止扫描 @@ -38,7 +38,7 @@ interface IBluetoothClient { * @param mac 需要连接蓝牙设备的地址 * @return 成功,返回连接设备的地址 */ - fun connect(mac: String?): Observable + fun connect(mac: String): Observable /** * 断开蓝牙连接, 释放蓝牙连接占用的蓝牙服务 diff --git a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothCommon.kt b/commonbt/src/main/java/com/common/bluetooth/service/BluetoothCommon.kt new file mode 100644 index 0000000..3122067 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/BluetoothCommon.kt @@ -0,0 +1,120 @@ +package com.common.bluetooth.service + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.content.Context +import android.content.pm.PackageManager +import android.util.Log +import com.common.bluetooth.BtConstants.BLUETOOTH_TYPE +import com.common.bluetooth.interfaces.IBluetoothSearch +import com.common.bluetooth.service.bt.BluetoothClassicBase +import com.common.bluetooth.service.bt.BluetoothClassicSearcher + +/** + * 通用蓝牙 + * + * @author wangym + * @since 2021-12-1 + */ +open class BluetoothCommon(val mContext: Context) { + val TAG = "BluetoothCommon" + var mBluetoothAdapter: BluetoothAdapter? = null + var mBluetoothManager: BluetoothManager? = null + var mBluetoothSearcher: IBluetoothSearch? = null + + /** + * 检查蓝牙设备是否支持 + * + * @param btType 蓝牙类型 + */ + fun checkBtDevice(btType: BLUETOOTH_TYPE): Boolean { + if (btType === BLUETOOTH_TYPE.BLE) { + // 检查当前手机是否支持ble 蓝牙 + if (!mContext.packageManager + .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) + ) { + Log.e(TAG, "not support ble device") + return false + } + } + + // For API level 18 and above, get a reference to BluetoothAdapter + // through BluetoothManager. + if (mBluetoothManager == null) { + mBluetoothManager = mContext + .getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + if (mBluetoothManager == null) { + Log.e(TAG, "Unable to initialize BluetoothManager.") + return false + } + } + if (mBluetoothAdapter == null) { + mBluetoothAdapter = mBluetoothManager?.adapter + if (mBluetoothAdapter == null) { + Log.e(TAG, "Unable to obtain a BluetoothAdapter.") + return false + } + } + return true + } + + /** + * 通过MAC地址获取设备对象 + * + * @param mac mac地址 + * @return 设备对象 + */ + fun getBtDeviceByMac(mac: String?): BluetoothDevice? { + if (mBluetoothAdapter == null) { + Log.e(TAG, "getBtDeviceByMac mBluetoothAdapter is null") + return null + } + return mBluetoothAdapter!!.getRemoteDevice(mac) + } + + /** + * 初始化BluetoothAdapter + * Initializes a reference to the local Bluetooth adapter. + * + * @return Return true if the initialization is successful. + */ + fun openBt(): Boolean { + if (mBluetoothAdapter == null) { + Log.e(TAG, "BluetoothManager do not init") + return false + } + return mBluetoothAdapter!!.isEnabled() || mBluetoothAdapter!!.enable() + } + + fun closeBt(): Boolean { + if (mBluetoothAdapter == null) { + Log.e(TAG, "BluetoothManager do not init") + return false + } + return mBluetoothAdapter!!.disable() + } + + /** + * 获取已配对蓝牙设备信息 + */ + fun getBondedDevices(): Set? { + return mBluetoothAdapter?.bondedDevices + } + + /** + * 获取蓝牙扫描对象 + * + * @return 蓝牙扫描对象 + */ + fun getBluetoothSearcher(): IBluetoothSearch { + if (mBluetoothSearcher == null) { + synchronized(BluetoothClassicBase::class.java) { + if (mBluetoothSearcher == null) { + mBluetoothSearcher = BluetoothClassicSearcher(mContext, mBluetoothAdapter) + } + } + } + return mBluetoothSearcher!! + } +} \ No newline at end of file diff --git a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothConnectService.java b/commonbt/src/main/java/com/common/bluetooth/service/BluetoothConnectService.java deleted file mode 100644 index 4631051..0000000 --- a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothConnectService.java +++ /dev/null @@ -1,519 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.common.bluetooth.service; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothServerSocket; -import android.bluetooth.BluetoothSocket; -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.util.Log; - -import com.common.bluetooth.BtConstants; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.UUID; - -/** - * This class does all the work for setting up and managing Bluetooth - * connections with other devices. It has a thread that listens for - * incoming connections, a thread for connecting with a device, and a - * thread for performing data transmissions when connected. - */ -public class BluetoothConnectService { - // Debugging - private static final String TAG = "BluetoothConnectService"; - - // Name for the SDP record when creating server socket - private static final String NAME_SECURE = "BluetoothChatSecure"; - private static final String NAME_INSECURE = "BluetoothChatInsecure"; - - // Unique UUID for this application - private final UUID CURRENT_UUID; - - // Member fields - private final BluetoothAdapter mAdapter; - private final Handler mHandler; - private AcceptThread mSecureAcceptThread; - private AcceptThread mInsecureAcceptThread; - private ConnectThread mConnectThread; - private ConnectedThread mConnectedThread; - private int mState; - private int mNewState; - - // Constants that indicate the current connection state - public static final int STATE_NONE = 0; // we're doing nothing - public static final int STATE_LISTEN = 1; // now listening for incoming connections - public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection - public static final int STATE_CONNECTED = 3; // now connected to a remote device - - /** - * Constructor. Prepares a new BluetoothChat session. - * - * @param context The UI Activity Context - * @param handler A Handler to send messages back to the UI Activity - */ - public BluetoothConnectService(Context context, Handler handler) { - CURRENT_UUID = BtConstants.INSTANCE.getUUid(context); - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mState = STATE_NONE; - mNewState = mState; - mHandler = handler; - } - - /** - * Update UI title according to the current state of the chat connection - */ - private synchronized void updateUserInterfaceTitle() { - mState = getState(); - Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mState); - mNewState = mState; - - // Give the new state to the Handler so the UI Activity can update - mHandler.obtainMessage(BtConstants.MESSAGE_STATE_CHANGE, mNewState, -1).sendToTarget(); - } - - /** - * Return the current connection state. - */ - public synchronized int getState() { - return mState; - } - - /** - * Start the chat service. Specifically start AcceptThread to begin a - * session in listening (server) mode. Called by the Activity onResume() - */ - public synchronized void start() { - Log.d(TAG, "start"); - - // Cancel any thread attempting to make a connection - if (mConnectThread != null) { - mConnectThread.cancel(); - mConnectThread = null; - } - - // Cancel any thread currently running a connection - if (mConnectedThread != null) { - mConnectedThread.cancel(); - mConnectedThread = null; - } - - // Start the thread to listen on a BluetoothServerSocket - if (mSecureAcceptThread == null) { - mSecureAcceptThread = new AcceptThread(false); - mSecureAcceptThread.start(); - } - if (mInsecureAcceptThread == null) { - mInsecureAcceptThread = new AcceptThread(false); - mInsecureAcceptThread.start(); - } - // Update UI title - updateUserInterfaceTitle(); - } - - /** - * Start the ConnectThread to initiate a connection to a remote device. - * - * @param device The BluetoothDevice to connect - */ - public synchronized void connect(BluetoothDevice device) { - Log.d(TAG, "connect to: " + device); - - // Cancel any thread attempting to make a connection - if (mState == STATE_CONNECTING) { - if (mConnectThread != null) { - mConnectThread.cancel(); - mConnectThread = null; - } - } - - // Cancel any thread currently running a connection - if (mConnectedThread != null) { - mConnectedThread.cancel(); - mConnectedThread = null; - } - - // Start the thread to connect with the given device - mConnectThread = new ConnectThread(device, false); - mConnectThread.start(); - // Update UI title - updateUserInterfaceTitle(); - } - - /** - * Start the ConnectedThread to begin managing a Bluetooth connection - * - * @param socket The BluetoothSocket on which the connection was made - * @param device The BluetoothDevice that has been connected - */ - public synchronized void connected(BluetoothSocket socket, BluetoothDevice - device, final String socketType) { - Log.d(TAG, "connected, Socket Type:" + socketType); - - // Cancel the thread that completed the connection - if (mConnectThread != null) { - mConnectThread.cancel(); - mConnectThread = null; - } - - // Cancel any thread currently running a connection - if (mConnectedThread != null) { - mConnectedThread.cancel(); - mConnectedThread = null; - } - - // Cancel the accept thread because we only want to connect to one device - if (mSecureAcceptThread != null) { - mSecureAcceptThread.cancel(); - mSecureAcceptThread = null; - } - if (mInsecureAcceptThread != null) { - mInsecureAcceptThread.cancel(); - mInsecureAcceptThread = null; - } - - // Start the thread to manage the connection and perform transmissions - mConnectedThread = new ConnectedThread(socket, socketType); - mConnectedThread.start(); - - // Send the name of the connected device back to the UI Activity - Message msg = mHandler.obtainMessage(BtConstants.MESSAGE_DEVICE_NAME); - Bundle bundle = new Bundle(); - bundle.putString(BtConstants.DEVICE_NAME, device.getName()); - msg.setData(bundle); - mHandler.sendMessage(msg); - // Update UI title - updateUserInterfaceTitle(); - } - - /** - * Stop all threads - */ - public synchronized void stop() { - Log.d(TAG, "stop"); - - if (mConnectThread != null) { - mConnectThread.cancel(); - mConnectThread = null; - } - - if (mConnectedThread != null) { - mConnectedThread.cancel(); - mConnectedThread = null; - } - - if (mSecureAcceptThread != null) { - mSecureAcceptThread.cancel(); - mSecureAcceptThread = null; - } - - if (mInsecureAcceptThread != null) { - mInsecureAcceptThread.cancel(); - mInsecureAcceptThread = null; - } - mState = STATE_NONE; - // Update UI title - updateUserInterfaceTitle(); - } - - /** - * Write to the ConnectedThread in an unsynchronized manner - * - * @param out The bytes to write - * @see ConnectedThread#write(byte[]) - */ - public void write(byte[] out) { - // Create temporary object - ConnectedThread r; - // Synchronize a copy of the ConnectedThread - synchronized (this) { - if (mState != STATE_CONNECTED) return; - r = mConnectedThread; - } - // Perform the write unsynchronized - r.write(out); - } - - /** - * Indicate that the connection attempt failed and notify the UI Activity. - */ - private void connectionFailed() { - // Send a failure message back to the Activity - Message msg = mHandler.obtainMessage(BtConstants.MESSAGE_TOAST); - Bundle bundle = new Bundle(); - bundle.putString(BtConstants.TOAST, "Unable to connect device"); - msg.setData(bundle); - mHandler.sendMessage(msg); - - mState = STATE_NONE; - // Update UI title - updateUserInterfaceTitle(); - - // Start the service over to restart listening mode - BluetoothConnectService.this.start(); - } - - /** - * Indicate that the connection was lost and notify the UI Activity. - */ - private void connectionLost() { - // Send a failure message back to the Activity - Message msg = mHandler.obtainMessage(BtConstants.MESSAGE_TOAST); - Bundle bundle = new Bundle(); - bundle.putString(BtConstants.TOAST, "Device connection was lost"); - msg.setData(bundle); - mHandler.sendMessage(msg); - - mState = STATE_NONE; - // Update UI title - updateUserInterfaceTitle(); - - // Start the service over to restart listening mode - BluetoothConnectService.this.start(); - } - - /** - * This thread runs while listening for incoming connections. It behaves - * like a server-side client. It runs until a connection is accepted - * (or until cancelled). - */ - private class AcceptThread extends Thread { - // The local server socket - private final BluetoothServerSocket mmServerSocket; - private String mSocketType; - - public AcceptThread(boolean secure) { - BluetoothServerSocket tmp = null; - mSocketType = secure ? "Secure" : "Insecure"; - - // Create a new listening server socket - try { - tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, - CURRENT_UUID); - } catch (IOException e) { - Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e); - } - mmServerSocket = tmp; - mState = STATE_LISTEN; - } - - public void run() { - Log.d(TAG, "Socket Type: " + mSocketType + - "BEGIN mAcceptThread" + this); - setName("AcceptThread" + mSocketType); - - BluetoothSocket socket = null; - - // Listen to the server socket if we're not connected - while (mState != STATE_CONNECTED) { - try { - // This is a blocking call and will only return on a - // successful connection or an exception - socket = mmServerSocket.accept(); - } catch (IOException e) { - Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e); - break; - } - - // If a connection was accepted - if (socket != null) { - synchronized (BluetoothConnectService.this) { - switch (mState) { - case STATE_LISTEN: - case STATE_CONNECTING: - // Situation normal. Start the connected thread. - connected(socket, socket.getRemoteDevice(), - mSocketType); - break; - case STATE_NONE: - case STATE_CONNECTED: - // Either not ready or already connected. Terminate new socket. - try { - socket.close(); - } catch (IOException e) { - Log.e(TAG, "Could not close unwanted socket", e); - } - break; - } - } - } - } - Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType); - - } - - public void cancel() { - Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this); - try { - mmServerSocket.close(); - } catch (IOException e) { - Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e); - } - } - } - - - /** - * This thread runs while attempting to make an outgoing connection - * with a device. It runs straight through; the connection either - * succeeds or fails. - */ - private class ConnectThread extends Thread { - private BluetoothSocket mmSocket; - private final BluetoothDevice mmDevice; - private String mSocketType; - - public ConnectThread(BluetoothDevice device, boolean secure) { - mmDevice = device; - BluetoothSocket tmp = null; - mSocketType = secure ? "Secure" : "Insecure"; - - // Get a BluetoothSocket for a connection with the - // given BluetoothDevice - try { - tmp = device.createRfcommSocketToServiceRecord( - CURRENT_UUID); - } catch (IOException e) { - Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e); - } - mmSocket = tmp; - mState = STATE_CONNECTING; - } - - public void run() { - Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType); - setName("ConnectThread" + mSocketType); - - // Always cancel discovery because it will slow down a connection - mAdapter.cancelDiscovery(); - - // Make a connection to the BluetoothSocket - try { - // This is a blocking call and will only return on a - // successful connection or an exception - mmSocket.connect(); - } catch (IOException e) { - // Unable to connect; close the socket and return. - Log.e(TAG, e.toString()); - try { - mmSocket.close(); - } catch (IOException ie) { - connectionFailed(); - } - return; - } - - // Reset the ConnectThread because we're done - synchronized (BluetoothConnectService.this) { - mConnectThread = null; - } - - // Start the connected thread - connected(mmSocket, mmDevice, mSocketType); - } - - public void cancel() { - try { - mmSocket.close(); - } catch (IOException e) { - Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e); - } - } - } - - /** - * This thread runs during a connection with a remote device. - * It handles all incoming and outgoing transmissions. - */ - private class ConnectedThread extends Thread { - private final BluetoothSocket mmSocket; - private final InputStream mmInStream; - private final OutputStream mmOutStream; - - public ConnectedThread(BluetoothSocket socket, String socketType) { - Log.d(TAG, "create ConnectedThread: " + socketType); - mmSocket = socket; - InputStream tmpIn = null; - OutputStream tmpOut = null; - - // Get the BluetoothSocket input and output streams - try { - tmpIn = socket.getInputStream(); - tmpOut = socket.getOutputStream(); - } catch (IOException e) { - Log.e(TAG, "temp sockets not created", e); - } - - mmInStream = tmpIn; - mmOutStream = tmpOut; - mState = STATE_CONNECTED; - } - - public void run() { - Log.i(TAG, "BEGIN mConnectedThread"); - byte[] buffer = new byte[1024]; - int bytes; - - // Keep listening to the InputStream while connected - while (mState == STATE_CONNECTED) { - try { - // Read from the InputStream - bytes = mmInStream.read(buffer); - - // Send the obtained bytes to the UI Activity - mHandler.obtainMessage(BtConstants.MESSAGE_READ, bytes, -1, buffer) - .sendToTarget(); - } catch (IOException e) { - Log.e(TAG, "disconnected", e); - connectionLost(); - break; - } - } - } - - /** - * Write to the connected OutStream. - * - * @param buffer The bytes to write - */ - public void write(byte[] buffer) { - try { - mmOutStream.write(buffer); - - // Share the sent message back to the UI Activity - mHandler.obtainMessage(BtConstants.MESSAGE_WRITE, -1, -1, buffer) - .sendToTarget(); - } catch (IOException e) { - Log.e(TAG, "Exception during write", e); - } - } - - public void cancel() { - try { - mmSocket.close(); - } catch (IOException e) { - Log.e(TAG, "close() of connect socket failed", e); - } - } - } -} diff --git a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeClient.java b/commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeClient.java deleted file mode 100644 index cee6db1..0000000 --- a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeClient.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.common.bluetooth.service; - -import android.annotation.SuppressLint; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothManager; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Handler; -import android.os.HandlerThread; -import android.util.Log; - -import com.common.bluetooth.BtConstants; -import com.common.bluetooth.interfaces.IBluetoothSearch; - -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 蓝牙操作管理类 - * - * @author wangym - * @since 2021-10-19 - */ -public class BluetoothLeClient { - @SuppressLint("StaticFieldLeak") - private static volatile BluetoothLeClient mInstance; - - private final static String TAG = "BluetoothLeClient"; - private final Context mContext; - private static Handler mBluetoothWorker; - private BluetoothAdapter mBluetoothAdapter; - private BluetoothManager mBluetoothManager; - private IBluetoothSearch mBluetoothSearcher; - - private final Map mGattConnectorMap - = new ConcurrentHashMap<>(); - - private BluetoothLeClient(Context context) { - mContext = context.getApplicationContext(); - - HandlerThread thread = new HandlerThread("bluetooth client worker"); - thread.start(); - mBluetoothWorker = new Handler(thread.getLooper()); - } - - public static BluetoothLeClient getInstance(Context context) { - if (mInstance == null) { - synchronized (BluetoothLeClient.class) { - if (mInstance == null) { - mInstance = new BluetoothLeClient(context); - } - } - } - - return mInstance; - } - - /** - * 检查蓝牙设备是否支持 - * - * @param btType 蓝牙类型 - */ - public boolean checkBtDevice(BtConstants.BLUETOOTH_TYPE btType) { - if (btType == BtConstants.BLUETOOTH_TYPE.BLE) { - // 检查当前手机是否支持ble 蓝牙 - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { - Log.e(TAG, "not support ble device"); - return false; - } - } - - // For API level 18 and above, get a reference to BluetoothAdapter - // through BluetoothManager. - if (mBluetoothManager == null) { - mBluetoothManager = (BluetoothManager) mContext - .getSystemService(Context.BLUETOOTH_SERVICE); - if (mBluetoothManager == null) { - Log.e(TAG, "Unable to initialize BluetoothManager."); - return false; - } - } - - if (mBluetoothAdapter == null) { - mBluetoothAdapter = mBluetoothManager.getAdapter(); - if (mBluetoothAdapter == null) { - Log.e(TAG, "Unable to obtain a BluetoothAdapter."); - return false; - } - } - return true; - } - - /** - * 初始化BluetoothAdapter - * Initializes a reference to the local Bluetooth adapter. - * - * @return Return true if the initialization is successful. - */ - public boolean openBt() { - if (mBluetoothAdapter == null) { - Log.e(TAG, "BluetoothManager do not init"); - return false; - } - return mBluetoothAdapter.isEnabled() || mBluetoothAdapter.enable(); - } - - public boolean closeBt() { - if (mBluetoothAdapter == null) { - Log.e(TAG, "BluetoothManager do not init"); - return false; - } - return mBluetoothAdapter.disable(); - } - - public IBluetoothSearch getBluetoothSearcher() { - if (mBluetoothSearcher == null) { - synchronized (BluetoothLeClient.class) { - if (mBluetoothSearcher == null) { - if (mBluetoothAdapter == null) { - String err = "cannot create BluetoothLeSearcher instance because not " + - "initialize, please call initialize() method"; - Log.e(TAG, err); - return null; - } - - mBluetoothSearcher = new BluetoothLeSearcher(mContext, mBluetoothAdapter, mBluetoothWorker); - } - } - } - - return mBluetoothSearcher; - } - - public BluetoothLeConnector getBluetoothLeConnector(String mac) { - BluetoothLeConnector result; - if ((result = mGattConnectorMap.get(mac)) != null) { - return result; - } - - result = new BluetoothLeConnector(mContext, mBluetoothAdapter, mac, mBluetoothWorker); - mGattConnectorMap.put(mac, result); - return result; - } - - public void cleanConnector(String mac) { - BluetoothLeConnector result; - if ((result = mGattConnectorMap.get(mac)) != null) { - mGattConnectorMap.remove(mac); - result.disconnect(); - result.setOnDataAvailableListener(null); - } - } - - /** - * 在不在需要连接蓝牙设备的时候, - * 或者生命周期暂停的时候调用这一个方法 - */ - public void cleanAllConnector() { - for (String mac : mGattConnectorMap.keySet()) { - cleanConnector(mac); - } - } - - /** - * 获取已配对蓝牙设备信息 - */ - public Set getBondedDevices() { - return mBluetoothAdapter.getBondedDevices(); - } -} diff --git a/commonbt/src/main/java/com/common/bluetooth/service/BLEClientService.java b/commonbt/src/main/java/com/common/bluetooth/service/ble/BLEClientService.java similarity index 98% rename from commonbt/src/main/java/com/common/bluetooth/service/BLEClientService.java rename to commonbt/src/main/java/com/common/bluetooth/service/ble/BLEClientService.java index da5b625..76669c7 100644 --- a/commonbt/src/main/java/com/common/bluetooth/service/BLEClientService.java +++ b/commonbt/src/main/java/com/common/bluetooth/service/ble/BLEClientService.java @@ -1,4 +1,4 @@ -package com.common.bluetooth.service; +package com.common.bluetooth.service.ble; import static com.common.bluetooth.BtConstants.CONNECT_SUCCESS; @@ -13,8 +13,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.common.bluetooth.BtConstants; -import com.common.bluetooth.BtUtils; -import com.common.bluetooth.adapter.BluetoothClientBLEAdapter; +import com.common.bluetooth.utils.BtUtils; import com.common.bluetooth.bean.CommonMsg; import com.common.bluetooth.callback.BLEClientListener; import com.common.bluetooth.callback.BaseResultCallback; @@ -29,7 +28,6 @@ import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; - /** * BLE客户端服务 * diff --git a/commonbt/src/main/java/com/common/bluetooth/service/BLEReceiveService.java b/commonbt/src/main/java/com/common/bluetooth/service/ble/BLEReceiveService.java similarity index 97% rename from commonbt/src/main/java/com/common/bluetooth/service/BLEReceiveService.java rename to commonbt/src/main/java/com/common/bluetooth/service/ble/BLEReceiveService.java index fc62fd0..414dfce 100644 --- a/commonbt/src/main/java/com/common/bluetooth/service/BLEReceiveService.java +++ b/commonbt/src/main/java/com/common/bluetooth/service/ble/BLEReceiveService.java @@ -1,4 +1,4 @@ -package com.common.bluetooth.service; +package com.common.bluetooth.service.ble; import android.app.Service; import android.bluetooth.BluetoothDevice; @@ -18,15 +18,12 @@ import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; -import android.text.TextUtils; import android.util.Log; import androidx.annotation.Nullable; import com.common.bluetooth.BtConstants; -import com.common.bluetooth.callback.BleMsgReceiverListener; - -import java.nio.charset.StandardCharsets; +import com.common.bluetooth.callback.MsgReceiverListener; /** * BLE接收服务 @@ -51,7 +48,7 @@ public class BLEReceiveService extends Service { /** * 蓝牙消息接收监听 */ - private BleMsgReceiverListener receiverListener; + private MsgReceiverListener receiverListener; /** * 远端设备 */ @@ -159,7 +156,7 @@ public class BLEReceiveService extends Service { } } - public void setMsgReceiveListener(BleMsgReceiverListener listener) { + public void setMsgReceiveListener(MsgReceiverListener listener) { receiverListener = listener; } diff --git a/commonbt/src/main/java/com/common/bluetooth/adapter/BluetoothClientBLEAdapter.java b/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothClientBLEAdapter.java similarity index 97% rename from commonbt/src/main/java/com/common/bluetooth/adapter/BluetoothClientBLEAdapter.java rename to commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothClientBLEAdapter.java index 71ad17a..baccdb0 100644 --- a/commonbt/src/main/java/com/common/bluetooth/adapter/BluetoothClientBLEAdapter.java +++ b/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothClientBLEAdapter.java @@ -1,4 +1,4 @@ -package com.common.bluetooth.adapter; +package com.common.bluetooth.service.ble; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; @@ -17,8 +17,6 @@ import com.common.bluetooth.exception.BluetoothSearchConflictException; import com.common.bluetooth.exception.BluetoothWriteException; import com.common.bluetooth.interfaces.IBluetoothClient; import com.common.bluetooth.interfaces.IBluetoothSearch; -import com.common.bluetooth.service.BluetoothLeClient; -import com.common.bluetooth.service.BluetoothLeConnector; import java.util.HashSet; import java.util.Set; @@ -53,7 +51,7 @@ public class BluetoothClientBLEAdapter implements IBluetoothClient { } @Override - public Observable search(final int millis, final boolean cancel) { + public Observable search(final long millis, final boolean cancel) { return Observable.create(new ObservableOnSubscribe() { @Override public void subscribe(@NonNull final ObservableEmitter emitter) { diff --git a/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeClient.java b/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeClient.java new file mode 100644 index 0000000..fd2a243 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeClient.java @@ -0,0 +1,79 @@ +package com.common.bluetooth.service.ble; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; + +import com.common.bluetooth.service.BluetoothCommon; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 蓝牙操作管理类 + * + * @author wangym + * @since 2021-10-19 + */ +public class BluetoothLeClient extends BluetoothCommon { + @SuppressLint("StaticFieldLeak") + private static volatile BluetoothLeClient mInstance; + + private final static String TAG = "BluetoothLeClient"; + private final Context mContext; + private static Handler mBluetoothWorker; + + private final Map mGattConnectorMap + = new ConcurrentHashMap<>(); + + private BluetoothLeClient(Context context) { + super(context.getApplicationContext()); + mContext = context.getApplicationContext(); + HandlerThread thread = new HandlerThread("bluetooth client worker"); + thread.start(); + mBluetoothWorker = new Handler(thread.getLooper()); + } + + public static BluetoothLeClient getInstance(Context context) { + if (mInstance == null) { + synchronized (BluetoothLeClient.class) { + if (mInstance == null) { + mInstance = new BluetoothLeClient(context); + } + } + } + + return mInstance; + } + + public BluetoothLeConnector getBluetoothLeConnector(String mac) { + BluetoothLeConnector result; + if ((result = mGattConnectorMap.get(mac)) != null) { + return result; + } + + result = new BluetoothLeConnector(mContext, getMBluetoothAdapter(), mac, mBluetoothWorker); + mGattConnectorMap.put(mac, result); + return result; + } + + public void cleanConnector(String mac) { + BluetoothLeConnector result; + if ((result = mGattConnectorMap.get(mac)) != null) { + mGattConnectorMap.remove(mac); + result.disconnect(); + result.setOnDataAvailableListener(null); + } + } + + /** + * 在不在需要连接蓝牙设备的时候, + * 或者生命周期暂停的时候调用这一个方法 + */ + public void cleanAllConnector() { + for (String mac : mGattConnectorMap.keySet()) { + cleanConnector(mac); + } + } +} diff --git a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeConnector.java b/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeConnector.java similarity index 99% rename from commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeConnector.java rename to commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeConnector.java index fbda5e7..932280f 100644 --- a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeConnector.java +++ b/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeConnector.java @@ -1,4 +1,4 @@ -package com.common.bluetooth.service; +package com.common.bluetooth.service.ble; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -23,7 +23,6 @@ import java.util.concurrent.atomic.AtomicLong; import io.reactivex.rxjava3.functions.Consumer; - /** * Service for managing connection and data communication with a GATT server * hosted on a given Bluetooth LE device. @@ -417,8 +416,8 @@ public class BluetoothLeConnector { try { action.accept(gattCharacteristic); - } catch (Throwable e) { - e.printStackTrace(); + } catch (Throwable throwable) { + throwable.printStackTrace(); } } diff --git a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeSearcher.java b/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeSearcher.java similarity index 99% rename from commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeSearcher.java rename to commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeSearcher.java index 6819967..fecb3fe 100644 --- a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothLeSearcher.java +++ b/commonbt/src/main/java/com/common/bluetooth/service/ble/BluetoothLeSearcher.java @@ -1,4 +1,4 @@ -package com.common.bluetooth.service; +package com.common.bluetooth.service.ble; import android.Manifest; import android.bluetooth.BluetoothAdapter; diff --git a/commonbt/src/main/java/com/common/bluetooth/service/bt/BTCClientService.java b/commonbt/src/main/java/com/common/bluetooth/service/bt/BTCClientService.java new file mode 100644 index 0000000..8c3f24c --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/bt/BTCClientService.java @@ -0,0 +1,102 @@ +package com.common.bluetooth.service.bt; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.common.bluetooth.BtConstants; +import com.common.bluetooth.callback.BtMsgListener; + +/** + * 经典蓝牙服务端 + * + * @author wangym + * @since 2021-10-29 + */ +public class BTCClientService extends Service { + private static final String TAG = "BTCClientService"; + /** + * 检点蓝牙服务端 + */ + private BluetoothClassicClient classicClient; + + /** + * 消息回调 + */ + private final BtMsgListener mMsgListener = new BtMsgListener() { + @Override + public void socketNotify(int state, @NonNull String msg) { + super.socketNotify(state, msg); + // 将内容消息透传给外部调用者 + if (msgListener != null) { + msgListener.socketNotify(state, msg); + } + } + }; + + /** + * 消息回调 + */ + private BtMsgListener msgListener = null; + + @Override + public void onCreate() { + super.onCreate(); + + Log.i(TAG, "initialize BluetoothManager"); + classicClient = new BluetoothClassicClient(this); + classicClient.checkBtDevice(BtConstants.BLUETOOTH_TYPE.CLASSIC); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return new ClassicClientBinder(); + } + + public class ClassicClientBinder extends Binder { + public BTCClientService getService() { + return BTCClientService.this; + } + } + + public void setMsgReceiveListener(BtMsgListener listener) { + msgListener = listener; + } + + public void connect(String mac) { + if (classicClient == null) { + Log.e(TAG, "classicClient not init success"); + return; + } + classicClient.setListener(mMsgListener); + classicClient.connect(mac); + } + + /** + * 向客户端发送通知 + * + * @param msg 消息 + */ + public void write(byte[] msg) { + Log.i(TAG, "write msg = " + new String(msg)); + if (classicClient != null && msg != null) { + Log.i(TAG, "---send msg start---"); + classicClient.sendMsg(msg); + Log.i(TAG, "---send msg end---"); + } else { + Log.e(TAG, "got msg null"); + } + } + + @Override + public boolean onUnbind(Intent intent) { + classicClient.close(); + return super.onUnbind(intent); + } +} diff --git a/commonbt/src/main/java/com/common/bluetooth/service/bt/BTCReceiverService.java b/commonbt/src/main/java/com/common/bluetooth/service/bt/BTCReceiverService.java new file mode 100644 index 0000000..9bc8c23 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/bt/BTCReceiverService.java @@ -0,0 +1,108 @@ +package com.common.bluetooth.service.bt; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.common.bluetooth.callback.BtMsgListener; + +import java.nio.charset.StandardCharsets; + +/** + * 经典蓝牙服务端 + * + * @author wangym + * @since 2021-10-29 + */ +public class BTCReceiverService extends Service { + private static final String TAG = "BTCReceiverService"; + /** + * 检点蓝牙服务端 + */ + private BluetoothClassicServer classicServer; + + /** + * 消息回调 + */ + private final BtMsgListener mMsgListener = new BtMsgListener() { + @Override + public void socketNotify(int state, @NonNull String msg) { + super.socketNotify(state, msg); + // 当客户端断开连接时,需要重新开始监听客户端的连接 + if (state == BtMsgListener.DISCONNECTED) { + if (classicServer != null) { + classicServer.listen(); + } + } else if (state == BtMsgListener.MSG) { + // 服务端只关心内容消息 + // 将内容消息透传给外部调用者 + if (msgListener != null) { + msgListener.socketNotify(state, msg); + } + } + } + }; + + /** + * 消息回调 + */ + private BtMsgListener msgListener = null; + + @Override + public void onCreate() { + super.onCreate(); + + Log.i(TAG, "initialize BluetoothManager"); + classicServer = new BluetoothClassicServer(this); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + if (classicServer != null) { + classicServer.setListener(mMsgListener); + classicServer.listen(); + } + return new ClassicReceiverBinder(); + } + + public class ClassicReceiverBinder extends Binder { + public BTCReceiverService getService() { + return BTCReceiverService.this; + } + } + + public void setMsgReceiveListener(BtMsgListener listener) { + msgListener = listener; + } + + /** + * 向客户端发送通知 + * + * @param msg 消息 + */ + public void write(byte[] msg) { + Log.i(TAG, "sendNotify msg = " + new String(msg)); + if (classicServer != null && msg != null) { + Log.i(TAG, "---send msg start---"); + classicServer.sendMsg(msg); + Log.i(TAG, "---send msg end---"); + } else { + Log.e(TAG, "got msg null"); + } + } + + @Override + public boolean onUnbind(Intent intent) { + if (classicServer != null) { + classicServer.setListener(null); + classicServer.close(); + } + return super.onUnbind(intent); + } +} diff --git a/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicBase.java b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicBase.java new file mode 100644 index 0000000..562b9f9 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicBase.java @@ -0,0 +1,259 @@ +package com.common.bluetooth.service.bt; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.content.Context; +import android.os.Environment; +import android.util.Log; + +import com.common.bluetooth.BtConstants; +import com.common.bluetooth.callback.BtMsgListener; +import com.common.bluetooth.service.BluetoothCommon; +import com.common.bluetooth.utils.BtUtils; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 客户端和服务端的基类,用于管理socket长连接 + * + * @author wangym + * @since 2.21-12-2 + */ +public class BluetoothClassicBase extends BluetoothCommon { + private static final String TAG = "BluetoothClassicBase"; + private static final String FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bluetooth/"; + private static final int FLAG_MSG = 0; //消息标记 + private static final int FLAG_FILE = 1; //文件标记 + + protected BluetoothSocket mSocket; + private DataOutputStream mOut; + private BtMsgListener mListener; + /** + * 是否在读取中 + */ + private final AtomicBoolean isRead = new AtomicBoolean(false); + /** + * 是否在发送中 + */ + private final AtomicBoolean isSending = new AtomicBoolean(false); + + /** + * 消息队列 + */ + private final LinkedBlockingQueue msgQueue = new LinkedBlockingQueue<>(); + + public BluetoothClassicBase(Context context) { + super(context); + } + + /** + * 设置消息的监听 + * + * @param mListener 监听 + */ + public void setListener(BtMsgListener mListener) { + this.mListener = mListener; + } + + /** + * 循环读取对方数据(若没有数据,则阻塞等待) + */ + void loopRead(BluetoothSocket socket) { + mSocket = socket; + try { + if (!mSocket.isConnected()) { + mSocket.connect(); + } + String mac = mSocket.getRemoteDevice().getAddress(); + notifyUI(BtMsgListener.CONNECTED, mac); + // 将链接成功的MAC地址保存下来 + BtUtils.INSTANCE.saveConnectMac(getMContext(), mac); + mOut = new DataOutputStream(mSocket.getOutputStream()); + DataInputStream in = new DataInputStream(mSocket.getInputStream()); + isRead.set(true); + while (isRead.get()) { //死循环读取 + switch (in.readInt()) { + case FLAG_MSG: //读取短消息 + String msg = in.readUTF(); + Log.d(TAG, "接收短消息:" + msg); + notifyUI(BtMsgListener.MSG, msg); + break; + case FLAG_FILE: //读取文件 + new File(FILE_PATH).mkdirs(); + String fileName = in.readUTF(); //文件名 + long fileLen = in.readLong(); //文件长度 + // 读取文件内容 + long len = 0; + int r; + byte[] b = new byte[4 * 1024]; + FileOutputStream out = new FileOutputStream(FILE_PATH + fileName); + Log.d(TAG, "正在接收文件(" + fileName + "),请稍后..."); + while ((r = in.read(b)) != -1) { + out.write(b, 0, r); + len += r; + if (len >= fileLen) { + break; + } + } + Log.d(TAG, "文件接收完成(存放在:" + FILE_PATH + ")"); + break; + } + } + } catch (Throwable e) { + close(); + } + } + + /** + * 发送短消息 + */ + public void sendMsg(byte[] msg) { + Log.d(TAG, "send msg"); + if (mSocket == null || !mSocket.isConnected()) { + Log.e(TAG, "sendMsg got socket is not connect"); + return; + } + msgQueue.add(msg); + if (checkSend()) { + Log.d(TAG, "sending wait"); + return; + } + synchronized (BluetoothClassicBase.class) { + isSending.set(true); + // 循环发送消息 + while (true) { + if (!doSendMsg()) { + break; + } + } + isSending.set(false); + } + } + + /** + * 发送消息 + */ + public boolean doSendMsg() { + final byte[] msgItem = msgQueue.poll(); + if (msgItem == null) { + Log.d(TAG, "doTranslate Queue is null"); + return false; + } + try { + mOut.writeInt(FLAG_MSG); //消息标记 + mOut.writeUTF(new String(msgItem, BtConstants.INSTANCE.getDEFAULT_CHARSET())); + mOut.flush(); + Log.d(TAG, "发送短消息:" + new String(msgItem, BtConstants.INSTANCE.getDEFAULT_CHARSET())); + } catch (Throwable e) { + close(); + isSending.set(false); + } + return true; + } + + /** + * 发送文件 + */ + public void sendFile(final String filePath) { + if (checkSend()) return; + isSending.set(true); + Executors.newCachedThreadPool().execute(new Runnable() { + @Override + public void run() { + try { + FileInputStream in = new FileInputStream(filePath); + File file = new File(filePath); + mOut.writeInt(FLAG_FILE); //文件标记 + mOut.writeUTF(file.getName()); //文件名 + mOut.writeLong(file.length()); //文件长度 + int r; + byte[] b = new byte[4 * 1024]; + notifyUI(BtMsgListener.MSG, "正在发送文件(" + filePath + "),请稍后..."); + while ((r = in.read(b)) != -1) + mOut.write(b, 0, r); + mOut.flush(); + notifyUI(BtMsgListener.MSG, "文件发送完成."); + } catch (Throwable e) { + close(); + } + isSending.set(false); + } + }); + } + + /** + * 关闭Socket连接 + */ + public void close() { + Log.d(TAG, "close"); + try { + String mac = ""; + isRead.set(false); + if (mSocket != null) { + mSocket.close(); + mac = mSocket.getRemoteDevice().getAddress(); + } + notifyUI(BtMsgListener.DISCONNECTED, mac); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + /** + * 无回调关闭Socket + */ + public void closeSilence(){ + Log.d(TAG, "closeSilence"); + try { + isRead.set(false); + if (mSocket != null) { + mSocket.close(); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + /** + * 当前设备与指定设备是否连接 + * + * @param mac 设备MAC地址 + * @return 是否已经和对应设备拦截 + */ + public boolean isConnected(String mac) { + BluetoothDevice dev = getBtDeviceByMac(mac); + boolean connected = (mSocket != null && mSocket.isConnected()); + if (dev == null) { + return connected; + } + return connected && mSocket.getRemoteDevice().equals(dev); + } + + /** + * 是否在发送中 + * + * @return 是否发送中 + */ + private synchronized boolean checkSend() { + return isSending.get(); + } + + /** + * 通知外部接收信息 + * + * @param state 消息类型 + * @param msg 消息内容 + */ + protected void notifyUI(final int state, final String msg) { + if (mListener != null) { + mListener.socketNotify(state, msg); + } + } +} diff --git a/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicClient.java b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicClient.java new file mode 100644 index 0000000..adb25b9 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicClient.java @@ -0,0 +1,56 @@ +package com.common.bluetooth.service.bt; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.content.Context; +import android.util.Log; + +import com.common.bluetooth.BtConstants; +import com.common.bluetooth.callback.BtMsgListener; + +import java.util.concurrent.Executors; + +/** + * 经典蓝牙客户端,与服务端建立长连接 + * + * @author wangym + * @since 2021-12-1 + */ +public class BluetoothClassicClient extends BluetoothClassicBase { + private static final String TAG = "BtClient"; + + public BluetoothClassicClient(Context context) { + super(context); + } + + /** + * 与远端设备建立长连接 + * + * @param mac 设备MAC + */ + public void connect(String mac) { + Log.d(TAG, "start connect mac:" + mac); + BluetoothDevice bluetoothDevice = getBtDeviceByMac(mac); + if (bluetoothDevice == null) { + Log.e(TAG, "connect got device is null : mac is " + mac); + notifyUI(BtMsgListener.DISCONNECTED, mac); + return; + } + if (isConnected(mac)) { + Log.i(TAG, "connected, no need to close socket"); + return; + } + closeSilence(); + try { +// final BluetoothSocket socket = dev.createRfcommSocketToServiceRecord(SPP_UUID); //加密传输,Android系统强制配对,弹窗显示配对码 + final BluetoothSocket socket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord( + BtConstants.INSTANCE.getCLASSIC_BT_UUID()); //明文传输(不安全),无需配对 + // 开启子线程 + Executors.newCachedThreadPool().execute(() -> { + loopRead(socket); //循环读取 + }); + } catch (Throwable e) { + close(); + } + } +} \ No newline at end of file diff --git a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothClassicSearcher.java b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicSearcher.java similarity index 96% rename from commonbt/src/main/java/com/common/bluetooth/service/BluetoothClassicSearcher.java rename to commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicSearcher.java index 2688e3e..cced34c 100644 --- a/commonbt/src/main/java/com/common/bluetooth/service/BluetoothClassicSearcher.java +++ b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicSearcher.java @@ -1,4 +1,4 @@ -package com.common.bluetooth.service; +package com.common.bluetooth.service.bt; import android.bluetooth.BluetoothAdapter; import android.content.Context; diff --git a/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicServer.java b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicServer.java new file mode 100644 index 0000000..6769ce2 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClassicServer.java @@ -0,0 +1,59 @@ +package com.common.bluetooth.service.bt; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothServerSocket; +import android.bluetooth.BluetoothSocket; +import android.content.Context; + +import com.common.bluetooth.BtConstants; + +import java.util.concurrent.Executors; + +/** + * 服务端监听和连接线程,只连接一个设备 + * + * @author wangym + * @version 2021-12-2 + */ +public class BluetoothClassicServer extends BluetoothClassicBase { + private static final String TAG = BluetoothClassicServer.class.getSimpleName(); + private BluetoothServerSocket mSSocket; + + BluetoothClassicServer(Context context) { + super(context); + } + + /** + * 监听客户端发起的连接 + */ + public void listen() { + try { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); +// mSSocket = adapter.listenUsingRfcommWithServiceRecord(TAG, SPP_UUID); //加密传输,Android强制执行配对,弹窗显示配对码 + mSSocket = adapter.listenUsingInsecureRfcommWithServiceRecord(TAG, + BtConstants.INSTANCE.getCLASSIC_BT_UUID()); //明文传输(不安全),无需配对 + // 开启子线程 + Executors.newCachedThreadPool().execute(() -> { + try { + BluetoothSocket socket = mSSocket.accept(); // 监听连接 + mSSocket.close(); // 关闭监听,只连接一个设备 + loopRead(socket); // 循环读取 + } catch (Throwable e) { + close(); + } + }); + } catch (Throwable e) { + close(); + } + } + + @Override + public void close() { + try { + mSSocket.close(); + } catch (Throwable e) { + e.printStackTrace(); + } + super.close(); + } +} \ No newline at end of file diff --git a/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClientClassicAdapter.kt b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClientClassicAdapter.kt new file mode 100644 index 0000000..076f227 --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/bt/BluetoothClientClassicAdapter.kt @@ -0,0 +1,98 @@ +package com.common.bluetooth.service.bt + +import android.bluetooth.BluetoothDevice +import com.common.bluetooth.BtConstants +import com.common.bluetooth.callback.BaseResultCallback +import com.common.bluetooth.interfaces.IBluetoothClient +import io.reactivex.rxjava3.core.Observable +import java.util.UUID + +/** + * 经典蓝牙适配器 + * + * @author wangym + * @since 2021-12-1 + */ +class BluetoothClientClassicAdapter(private var mClient: BluetoothClassicClient) : + IBluetoothClient { + val TAG = "BluetoothClientAdapter" + + override fun search(millis: Long, cancel: Boolean): Observable { + return Observable.create { + mClient.getBluetoothSearcher().startScan(millis, null) + it.onComplete() + } + } + + override fun stopSearch() { + mClient.getBluetoothSearcher().stopScan() + } + + override fun connect(mac: String): Observable { + return Observable.create { + if (!mClient.isConnected((mac))) { + mClient.connect(mac) + } + it.onNext(mac) + it.onComplete() + } + } + + override fun disconnect(mac: String) { + mClient.close() + } + + override fun write( + mac: String, + service: UUID, + characteristic: UUID, + values: ByteArray + ): Observable? { + return Observable.create { + if (!mClient.isConnected((mac))) { + mClient.sendMsg(values) + } + it.onNext(mac) + it.onComplete() + } + } + + override fun checkBluetoothDevice(type: BtConstants.BLUETOOTH_TYPE): Boolean { + return mClient.checkBtDevice(type) + } + + override fun openBluetooth() { + mClient.openBt() + } + + override fun closeBluetooth(): Boolean { + return mClient.closeBt() + } + + override fun getBondedDevices(): Set? { + return mClient.getBondedDevices() + } + + override fun registerNotify( + mac: String, + service: UUID, + characteristic: UUID, + callback: BaseResultCallback? + ): Observable { + return Observable.create {} + } + + override fun unRegisterNotify( + mac: String, + service: UUID, + characteristic: UUID + ): Observable? { + return null + } + + override fun clean(mac: String) { + } + + override fun cleanAll() { + } +} \ No newline at end of file diff --git a/commonbt/src/main/java/com/common/bluetooth/service/bt/BtReceiver.java b/commonbt/src/main/java/com/common/bluetooth/service/bt/BtReceiver.java new file mode 100644 index 0000000..b1fe75f --- /dev/null +++ b/commonbt/src/main/java/com/common/bluetooth/service/bt/BtReceiver.java @@ -0,0 +1,94 @@ +package com.common.bluetooth.service.bt; + +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.util.Log; + +/** + * 监听蓝牙广播-各种状态 + */ +public class BtReceiver extends BroadcastReceiver { + private static final String TAG = BtReceiver.class.getSimpleName(); + private final Listener mListener; + + public BtReceiver(Context cxt, Listener listener) { + mListener = listener; + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//蓝牙开关状态 + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//蓝牙开始搜索 + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//蓝牙搜索结束 + + filter.addAction(BluetoothDevice.ACTION_FOUND);//蓝牙发现新设备(未配对的设备) + filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);//在系统弹出配对框之前(确认/输入配对码) + filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//设备配对状态改变 + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);//最底层连接建立 + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);//最底层连接断开 + + filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); //BluetoothAdapter连接状态 + filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); //BluetoothHeadset连接状态 + filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); //BluetoothA2dp连接状态 + cxt.registerReceiver(this, filter); + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action == null) + return; + Log.i(TAG, "===" + action); + BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (dev != null) + Log.i(TAG, "BluetoothDevice: " + dev.getName() + ", " + dev.getAddress()); + switch (action) { + case BluetoothAdapter.ACTION_STATE_CHANGED: + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0); + Log.i(TAG, "STATE: " + state); + break; + case BluetoothAdapter.ACTION_DISCOVERY_STARTED: + break; + case BluetoothAdapter.ACTION_DISCOVERY_FINISHED: + break; + + case BluetoothDevice.ACTION_FOUND: + short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MAX_VALUE); + Log.i(TAG, "EXTRA_RSSI:" + rssi); + mListener.foundDev(dev); + break; + case BluetoothDevice.ACTION_PAIRING_REQUEST: //在系统弹出配对框之前,实现自动配对,取消系统配对框 + /*try { + abortBroadcast();//终止配对广播,取消系统配对框 + boolean ret = dev.setPin("1234".getBytes()); //设置PIN配对码(必须是固定的) + } catch (Exception e) { + e.printStackTrace(); + }*/ + break; + case BluetoothDevice.ACTION_BOND_STATE_CHANGED: + Log.i(TAG, "BOND_STATE: " + intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 0)); + break; + case BluetoothDevice.ACTION_ACL_CONNECTED: + break; + case BluetoothDevice.ACTION_ACL_DISCONNECTED: + break; + + case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED: + Log.i(TAG, "CONN_STATE: " + intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 0)); + break; + case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: + Log.i(TAG, "CONN_STATE: " + intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0)); + break; + case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: + Log.i(TAG, "CONN_STATE: " + intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, 0)); + break; + } + } + + public interface Listener { + void foundDev(BluetoothDevice dev); + } +} \ No newline at end of file diff --git a/commonbt/src/main/java/com/common/bluetooth/BtUtils.kt b/commonbt/src/main/java/com/common/bluetooth/utils/BtUtils.kt similarity index 99% rename from commonbt/src/main/java/com/common/bluetooth/BtUtils.kt rename to commonbt/src/main/java/com/common/bluetooth/utils/BtUtils.kt index e17ee9f..bc73922 100644 --- a/commonbt/src/main/java/com/common/bluetooth/BtUtils.kt +++ b/commonbt/src/main/java/com/common/bluetooth/utils/BtUtils.kt @@ -1,4 +1,4 @@ -package com.common.bluetooth +package com.common.bluetooth.utils import android.content.Context import android.content.SharedPreferences @@ -113,6 +113,11 @@ object BtUtils { */ const val MOD_I_CN = "6" + /** + * 设置 + */ + const val MOD_S = "7" + /** * 中间包 */ diff --git a/commonbt/src/main/java/com/common/bluetooth/view/BtDeviceListAdapter.java b/commonbt/src/main/java/com/common/bluetooth/view/BtDeviceListAdapter.java index 1fb62df..ab42f3e 100644 --- a/commonbt/src/main/java/com/common/bluetooth/view/BtDeviceListAdapter.java +++ b/commonbt/src/main/java/com/common/bluetooth/view/BtDeviceListAdapter.java @@ -33,13 +33,13 @@ public class BtDeviceListAdapter extends RecyclerView.Adapter + +