parent
d05ddca018
commit
2e58b77dcc
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.common.bluetooth.callback
|
||||
|
||||
/**
|
||||
* BT服务监听
|
||||
*
|
||||
* @author wangym
|
||||
* @since 2021-12-9
|
||||
*/
|
||||
interface BtServiceListener {
|
||||
/**
|
||||
* 服务准备完毕
|
||||
*/
|
||||
fun onServiceReady()
|
||||
|
||||
/**
|
||||
* 服务断开
|
||||
*/
|
||||
fun onServiceDisConnected()
|
||||
}
|
@ -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<BluetoothDevice>? {
|
||||
return mBluetoothAdapter?.bondedDevices
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取蓝牙扫描对象
|
||||
*
|
||||
* @return 蓝牙扫描对象
|
||||
*/
|
||||
fun getBluetoothSearcher(): IBluetoothSearch {
|
||||
if (mBluetoothSearcher == null) {
|
||||
synchronized(BluetoothClassicBase::class.java) {
|
||||
if (mBluetoothSearcher == null) {
|
||||
mBluetoothSearcher = BluetoothClassicSearcher(mContext, mBluetoothAdapter)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mBluetoothSearcher!!
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.common.bluetooth.service;
|
||||
package com.common.bluetooth.service.ble;
|
||||
|
||||
import android.Manifest;
|
||||
import android.bluetooth.BluetoothAdapter;
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.common.bluetooth.service;
|
||||
package com.common.bluetooth.service.bt;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
@ -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<BluetoothDevice> {
|
||||
return Observable.create {
|
||||
mClient.getBluetoothSearcher().startScan(millis, null)
|
||||
it.onComplete()
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopSearch() {
|
||||
mClient.getBluetoothSearcher().stopScan()
|
||||
}
|
||||
|
||||
override fun connect(mac: String): Observable<String> {
|
||||
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<String>? {
|
||||
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<BluetoothDevice>? {
|
||||
return mClient.getBondedDevices()
|
||||
}
|
||||
|
||||
override fun registerNotify(
|
||||
mac: String,
|
||||
service: UUID,
|
||||
characteristic: UUID,
|
||||
callback: BaseResultCallback<ByteArray>?
|
||||
): Observable<String> {
|
||||
return Observable.create {}
|
||||
}
|
||||
|
||||
override fun unRegisterNotify(
|
||||
mac: String,
|
||||
service: UUID,
|
||||
characteristic: UUID
|
||||
): Observable<String>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun clean(mac: String) {
|
||||
}
|
||||
|
||||
override fun cleanAll() {
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue