The Bluetooth chat software based on Android Classic Bluetooth currently only supports one-to-one real-time communication, file transfer, friend addition, friend group, and friend online status update. The message sending mode supports text and emoticons.
Project address: github.com/xiaoyaoyou1… (Fork is welcome)
prospects
As a small range wireless connection technology, Bluetooth technology can realize convenient, flexible, safe, low-cost and low-power data and voice communication between devices. It is one of the mainstream technologies to realize wireless personal LAN at present. At the same time, the Bluetooth system works in the way of self-group networking. Each Bluetooth device can realize the function of route selection in the network, which can form a mobile self-group network. Bluetooth’s features, which in many ways match the concepts of Ad Hoc and WPAN, show its true potential. Moreover, the connection of Bluetooth with other networks can lead to a wider range of applications, such as access to the Internet, PSTN or public mobile networks, which can make the application easier or bring greater benefits to users.
Bluetooth chat as a local area network chat software, in the office intensive, to achieve rapid and stable real-time communication or more practical value. At present, Bluetooth technology develops rapidly, the transmission rate of 5.0 has reached 2Mbps, the transmission level has reached lossless level, and the effective working distance can reach 300 meters. Bluetooth networking technology is also being further updated. I believe that it will not be long before a very mature scheme comes out, so that it can realize the real-time chat function of multiple people online. Break the boundaries of one-to-many real-time chat.
1. Master-slave relationship of Bluetooth communication According to Bluetooth technology, one pair of devices must play the role of master and the other must play the role of slave when they communicate with each other. During communication, the master terminal must search and initiate pairing. In theory, one Bluetooth primary device can communicate with seven Bluetooth secondary devices simultaneously. A device with Bluetooth communication function, can switch between two roles, usually working in slave mode, waiting for other main devices to connect, when necessary, switch to the main mode, call other devices. When a Bluetooth device initiates a call in the main mode, it needs to know the bluetooth address and pairing password of the other party. After the pairing is complete, it can initiate a call directly. 2. Bluetooth Call Process When the bluetooth host initiates a call, the first step is to search for the bluetooth devices that can be searched around. The primary device pairs with the secondary Bluetooth device after finding the secondary Bluetooth device. In this case, you need to enter the PIN code of the secondary Device. Some devices do not need to enter the PIN code. After the pairing is complete, the bluetooth remote device records the trust information of the primary device. Then, the primary device can initiate a call to the secondary device. The paired device does not need to be re-paired for the next call. A paired device, as the secondary Bluetooth device, can also initiate a link request, but the Bluetooth module for data communication generally does not initiate a call. After the link is established, the primary and secondary ends can implement bidirectional data or voice communication. In the communication state, both the primary and secondary devices can initiate disconnection to disconnect the Bluetooth link. 3, bluetooth one-to-one serial data transmission applications Bluetooth data transfer applications, the one-to-one serial data communication is one of the most common application of bluetooth device is set in advance before they go out between two bluetooth pairing information, primary end entities from the end device PIN number, address, etc., on both ends of the equipment, electric chain automatically built, transparent serial transmission, without peripheral circuit operation. In one-to-one application, the secondary device can be set to two types. One is the silent state, that is, it can only communicate with the specified primary device and cannot be searched by other Bluetooth devices. The second is the development state, which can be searched by the specified host or other Bluetooth devices.
Functions overview
Bluetooth chat function is mainly divided into the following modules: message module, friend module and personal module.
Support one-to-one, one-to-many, many-to-many real-time chat, can transfer text, expressions, pictures, files and so on. You can send offline messages when the peer party is offline and push messages to the peer party when the peer party is online. Messages Support historical message storage and viewing.
Friends module
Support nearby friends to add, delete friends, friends group display, friends up and down reminding, friends nickname and group name modification.
Display personal information, including nicknames, images, joining time, etc.This module has not been implemented yet. At present, its main functions include one-to-one real-time chat, transmission of text, expression and files, and support to add, delete and group friends. The following describes the bluetooth communication process that has been realized.
Operation process
Code implementation:
Private void findDevice(){// Get the saved pairing device Set<BluetoothDevice> pairedDevices = BluetoothAdapter.getDefaultAdapter().getBondedDevices(); if (pairedDevices.size() > 0) { mGroupFriendListData.clear(); GroupInfo groupInfo = new GroupInfo(); groupInfo.setGroupName(BluetoothAdapter.getDefaultAdapter().getName()); List<FriendInfo> friendInfoList = new ArrayList<>(); for (BluetoothDevice device : pairedDevices) { FriendInfo friendInfo = new FriendInfo(); friendInfo.setIdentificationName(device.getName()); friendInfo.setDeviceAddress(device.getAddress()); friendInfo.setFriendNickName(device.getName()); friendInfo.setOnline(false); friendInfo.setJoinTime(DateTime.getStringByFormat(new Date(), DateTime.DEFYMDHMS)); friendInfo.setBluetoothDevice(device); friendInfoList.add(friendInfo); } groupInfo.setFriendList(friendInfoList); groupInfo.setOnlineNumber(0); mGroupFriendListData.add(groupInfo); mGroupFriendAdapter.setGroupInfoList(mGroupFriendListData); }}
Copy the code
Example friend list diagram:
Enable device discoverability
To make the local device discoverable by other devices, call the startActivityForResult(Intent, int) method with ACTION_REQUEST_DISCOVERABLE action Intent. This method makes a request to the system Settings to enable discoverable mode. By default, the device is in discoverable mode for 120 seconds. You can define different durations by adding the EXTRA_DISCOVERABLE_DURATION additional field to an Intent object. The maximum duration that an application can set is 3600 seconds, with 0 meaning the device is always discoverable. Any value less than 0 or greater than 3600 seconds is automatically set to 120 seconds. For example, the following code sets the duration to 300 seconds:
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
Copy the code
A dialog box is displayed when the application user enables the discoverable mode of the device. If the response is “Yes,” the device’s discoverable mode lasts for the specified time, and your Activity receives a call to the onActivityResult() callback method with the resulting code equal to the discoverable device duration. If the user responds “No” or an error occurs, the resulting code is equal to RESULT_CANCELED. In discoverable mode, the device quietly leaves the mode in place for a specified period of time. If you want to be notified when the discoverable mode changes, you can register an Intent broadcast of type ACTION_SCAN_MODE_CHANGED. The EXTRA_SCAN_MODE and EXTRA_PREVIOUS_SCAN_MODE additional fields are included in the Intent object, which tell you the old and new scan modes, respectively. Each of their possible values is: SCAN_MODE_CONNECTABLE_DISCOVERABLE, SCAN_MODE_CONNECTABLE, or SCAN_MODE_NONE, which indicate whether the device is in discoverable mode or in discoverable mode but can still receive connections, respectively, Or it cannot receive connections in discoverable mode. If you want to initialize a connection to a remote device, you do not need to enable device revisibility. Discoverability needs to be enabled only if you want your application to receive input connections as a server, because remote devices must be able to discover it before connecting to your device.
A simple call to the startDiscovery() method can begin device discovery. The process is asynchronous, and the method immediately returns a Boolean value indicating whether the discovery process was successfully started. The discovery process typically takes about 12 seconds to query the scan, followed by obtaining the Bluetooth name of each device discovered by the scan.
public class ScanBroadcastReceiver extends BroadcastReceiver { private IScanCallback<BluetoothDevice> scanCallback; private final Map<String, BluetoothDevice> mDeviceMap = new HashMap<>(); public ScanBroadcastReceiver(IScanCallback<BluetoothDevice> scanCallback) { this.scanCallback = scanCallback; } @Override public void onReceive(Context context, Intent intent) { if (scanCallback == null) { return; } if(intent.getAction().equals(bluetoothdevice.action_found)){BluetoothDevice BluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (bluetoothDevice == null) { return; } if (! mDeviceMap.containsKey(bluetoothDevice.getAddress())) { mDeviceMap.put(bluetoothDevice.getAddress(), bluetoothDevice); } scanCallback.discoverDevice(bluetoothDevice); }else if(intent.getAction().equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) deviceList = new ArrayList<>(mDeviceMap.values()); if(deviceList ! = null && deviceList.size() > 0){ scanCallback.scanFinish(deviceList); } else{ scanCallback.scanTimeout(); }}}}
Copy the code
Example graph of searching friends:
public class PairBroadcastReceiver extends BroadcastReceiver { private IPairCallback pairCallback; public PairBroadcastReceiver(IPairCallback pairCallback) { this.pairCallback = pairCallback; } @Override public void onReceive(Context context, Intent.getaction ().equals(intent.action_bond_state_changed)){intent.getAction().equals(intent.action_bond_state_changed) Updated equipment list information (matching) BluetoothDevice device = intent. GetParcelableExtra (BluetoothDevice. EXTRA_DEVICE); if(device ! = null){ resolveBondingState(device.getBondState()); } } } private void resolveBondingState(final int bondState) { if (pairCallback == null) { return; } Switch (bondState) {case BluetoothDevice.BOND_BONDED:// Paircallback.bonded (); break; Case bluetoothDevice.bond_bonding :// Pairing paircallback.bonding (); break; Case bluetoothdevice.bond_none :// Unpaired paircallback. unBonded(); break; default: pairCallback.bondFail(); break; }}}
Copy the code
Sample graph of pairing information:Sample diagram of pairing process:
Connect devices (friends establish communication channels)
When you want to connect two devices, one must act as a server by holding an open BluetoothServerSocket object. The purpose of the service socket is to listen for incoming connection requests and, when a connection request is received, provide a BluetoothSocket connection object. The BluetoothServerSocket can (and should) be deprecated when you get the BluetoothServerSocket object, unless you want to receive more connections. The following service is to establish a socket and receive a link to the basic process: 1, call listenUsingRfcommWithServiceRecord (String, UUID) method to obtain a BluetoothServerSocket object. The String argument in this method is an identifiable name of your server, which the system automatically writes to the Service Discovery Protocol (SDP) database entity on the device (the name is arbitrary, and you can simply use the name of your application). The UUID parameter is also included in the SDP entity and is the basic protocol for connecting to the client device. That is, when a client attempts to connect to a server, it carries a UUID that is unique to the server it wants to connect to. Connections can only be accepted if these UUID’s match exactly. 2. Initiate the connection request by calling the Accept () method. This is a blocking call. This method returns only if the connection is accepted or if an exception occurs. A connection is accepted only if the UUID carried by the remote device sending the connection request matches a UUID registered by the listening service socket. The accept() method returns a connected BluetoothSocket object. 3. Call close() unless you want to receive other connections. This method frees the service socket and all the resources it consumes, but does not close the BluetoothSocket object returned by the connected accept() method. Unlike TCP/IP, each RFCOMM channel is only allowed to connect to one client at a time, so in most cases it makes sense to call the close() method of the BluetoothServerSocket object immediately after receiving a connection socket. Here are the listening threads implemented by the above procedure:
public class AcceptThread extends Thread {
private BluetoothChatHelper mHelper;
private final BluetoothServerSocket mServerSocket;
private String mSocketType;
public AcceptThread(BluetoothChatHelper bluetoothChatHelper, boolean secure) {
mHelper = bluetoothChatHelper;
BluetoothServerSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
try {
if (secure) {
tmp = mHelper.getAdapter().listenUsingRfcommWithServiceRecord(ChatConstant.NAME_SECURE, ChatConstant.UUID_SECURE);
} else {
tmp = mHelper.getAdapter().listenUsingInsecureRfcommWithServiceRecord(ChatConstant.NAME_INSECURE, ChatConstant.UUID_INSECURE);
}
} catch (IOException e) {
BleLog.e("Socket Type: " + mSocketType + "listen() failed", e);
}
mServerSocket = tmp;
}
public void run() {
BleLog.i("Socket Type: " + mSocketType + "BEGIN mAcceptThread" + this);
setName("AcceptThread" + mSocketType);
BluetoothSocket socket = null;
while (mHelper.getState() != com.vise.basebluetooth.common.State.STATE_CONNECTED) {
try {
BleLog.i("wait new socket:" + mServerSocket);
socket = mServerSocket.accept();
} catch (IOException e) {
BleLog.e("Socket Type: " + mSocketType + " accept() failed", e);
break;
}
if (socket != null) {
synchronized (this) {
if(mHelper.getState() == com.vise.basebluetooth.common.State.STATE_LISTEN
|| mHelper.getState() == com.vise.basebluetooth.common.State.STATE_CONNECTING){
BleLog.i("mark CONNECTING");
mHelper.connected(socket, socket.getRemoteDevice(), mSocketType);
} else if(mHelper.getState() == com.vise.basebluetooth.common.State.STATE_NONE
|| mHelper.getState() == com.vise.basebluetooth.common.State.STATE_CONNECTED){
try {
socket.close();
} catch (IOException e) {
BleLog.e("Could not close unwanted socket", e);
}
}
}
}
}
BleLog.i("END mAcceptThread, socket Type: " + mSocketType);
}
public void cancel() {
BleLog.i("Socket Type" + mSocketType + "cancel " + this);
try {
mServerSocket.close();
} catch (IOException e) {
BleLog.e("Socket Type" + mSocketType + "close() of server failed", e);
}
}
}
Copy the code
The following is a basic connection process: 1, by calling the BluetoothDevice of createRfcommSocketToServiceRecord (UUID) method, get a BluetoothSocket object. This method initializes a BluetoothSocket object connected to the BluetoothDevice object. The UUID parameter passed to this method must match the UUID used by the server device to open the BluetoothServerSocket object. Simply hardcode it in your application and if it matches, the server and client code can apply the BluetoothSocket object. 2. Initialize the connection by calling connect(). In this call, an SDP query is performed on a remote device to find a matching UUID. If the query is successful and the remote device receives the connection request, it shares the USE of the RFCOMM channel during the connection and the connect() method returns. This method is a blocking call. If, for some reason, the connection fails or times out (after about 12 seconds), an exception is thrown. Here is the thread that implements the above process:
public class ConnectThread extends Thread { private BluetoothChatHelper mHelper; private final BluetoothSocket mSocket; private final BluetoothDevice mDevice; private String mSocketType; public ConnectThread(BluetoothChatHelper bluetoothChatHelper, BluetoothDevice device, boolean secure) { mHelper = bluetoothChatHelper; mDevice = device; BluetoothSocket tmp = null; mSocketType = secure ? "Secure" : "Insecure"; try { if (secure) { tmp = device.createRfcommSocketToServiceRecord(ChatConstant.UUID_SECURE); } else { tmp = device.createInsecureRfcommSocketToServiceRecord(ChatConstant.UUID_INSECURE); } } catch (IOException e) { BleLog.e("Socket Type: " + mSocketType + "create() failed", e); } mSocket = tmp; } public void run() { BleLog.i("BEGIN mConnectThread SocketType:" + mSocketType); setName("ConnectThread" + mSocketType); mHelper.getAdapter().cancelDiscovery(); try { mSocket.connect(); } catch (IOException e) { try { mSocket.close(); } catch (IOException e2) { BleLog.e("unable to close() " + mSocketType + " socket during connection failure", e2); } mHelper.connectionFailed(); return; } synchronized (this) { mHelper.setConnectThread(null); } mHelper.connected(mSocket, mDevice, mSocketType); } public void cancel() { try { mSocket.close(); } catch (IOException e) { BleLog.e("close() of connect " + mSocketType + " socket failed", e); }}}
Copy the code
The cancelDiscovery() method is called before the connection is established. This method should always be called before connecting, and it is safe to not actually check whether the Bluetooth discovery processing is running (if you want to check, call the isparent () method).
When you have successfully connected two (or more) devices, each of them has a BluetoothSocket object connected to it. This is a good start because you can share data between devices. The process of transmitting arbitrary data using BluetoothSocket objects is simple: 1. GetInputStream () and getOutputStream() are used to obtain InputStream and OutputStream objects that handle transmission tasks through sockets. 2. Use the read(byte[]) and write (byte[]) methods to read and write data in the stream. The following is the communication thread to achieve the above process:
public class ConnectedThread extends Thread { private final BluetoothChatHelper mHelper; private final BluetoothSocket mSocket; private final InputStream mInStream; private final OutputStream mOutStream; public ConnectedThread(BluetoothChatHelper bluetoothChatHelper, BluetoothSocket socket, String socketType) { BleLog.i("create ConnectedThread: " + socketType); mHelper = bluetoothChatHelper; mSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { BleLog.e("temp sockets not created", e); } mInStream = tmpIn; mOutStream = tmpOut; } public void run() { BleLog.i("BEGIN mConnectedThread"); int bytes; byte[] buffer = new byte[1024]; // Keep listening to the InputStream while connected while (true) { try { bytes = mInStream.read(buffer); byte[] data = new byte[bytes]; System.arraycopy(buffer, 0, data, 0, data.length); mHelper.getHandler().obtainMessage(ChatConstant.MESSAGE_READ, bytes, -1, data).sendToTarget(); } catch (IOException e) { BleLog.e("disconnected", e); mHelper.start(false); break; } } } public void write(byte[] buffer) { if(mSocket.isConnected()){ try { mOutStream.write(buffer); mHelper.getHandler().obtainMessage(ChatConstant.MESSAGE_WRITE, -1, -1, buffer).sendToTarget(); } catch (IOException e) { BleLog.e("Exception during write", e); } } } public void cancel() { try { mSocket.close(); } catch (IOException e) { BleLog.e("close() of connect socket failed", e); }}}
Copy the code
Example diagram of sending a message:Send emoticon sample image:Example diagram of sending files: