So I've made this app, where I find all BLE Devices with a name. But how can I make one of the specific fields, clickable and automatic connect to the device, so I can start writing/reading from it?
Adapter
public class ListAdapter_BTLE_Devices extends ArrayAdapter<BTLE_Device> {
Activity activity;
int layoutResourceID;
ArrayList<BTLE_Device> devices;
public ListAdapter_BTLE_Devices(Activity activity, int resource, ArrayList<BTLE_Device> objects) {
super(activity.getApplicationContext(), resource, objects);
this.activity = activity;
layoutResourceID = resource;
devices = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater =
(LayoutInflater) activity.getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(layoutResourceID, parent, false);
}
BTLE_Device device = devices.get(position);
String name = device.getName();
String address = device.getAddress();
int rssi = device.getRSSI();
TextView BLE_name = (TextView) convertView.findViewById(R.id.BLE_name);
if (name != null && name.length() > 0) {
BLE_name.setText(device.getName());
}
else {
BLE_name.setText("No Name");
}
TextView BLE_rssi = (TextView) convertView.findViewById(R.id.BLE_rssi);
BLE_rssi.setText("RSSI: " + Integer.toString(rssi));
TextView BLE_macaddr = (TextView) convertView.findViewById(R.id.BLE_macaddr);
if (address != null && address.length() > 0) {
BLE_macaddr.setText("MAC-addr: "+device.getAddress());
}
else {
BLE_macaddr.setText("No Address");
}
return convertView;
}
}
EDIT
I think i might be connected to the GATT now, so what I've done is..
To start with i get the MAC-addr from the Mainactivity and then I saved it in a intent, and started another activity onclick.
Here I did the follwing
DeviceAddress = intent.getStringExtra(MainActivity.EXTRAS_BLE_ADDRESS);
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(DeviceAddress);
device.connectGatt(this, false, mGattCallback);
and when I call connectGatt it prints the message Log.d(TAG, "Connection State: 1");, is this the right way to do it?
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.d(TAG, "Connection State Change: "+status+" -> "+connectionState(newState));
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
/*
* Once successfully connected, we must next discover all the services on the
* device before we can read and write their characteristics.
*/
Log.d(TAG, "Connection State: 1");
gatt.discoverServices();
} else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {
/*
* If at any point we disconnect, send a message to clear the weather values
* out of the UI
*/
Log.d(TAG, "Connection State: 2");
} else if (status != BluetoothGatt.GATT_SUCCESS) {
/*
* If there is a failure at any stage, simply disconnect
*/
Log.d(TAG, "Connection State: 3");
gatt.disconnect();
}
}
If you have problems with Bluetooth LE I suggest you to use my bluetooth le library (don't reinvent the wheel, it tooks me about 3/4 months to make the library, a bluetooth le communication can be really tricky to make), it is open source so you can also see the code for having an example of implementation, I link you the github page: https://github.com/niedev/BluetoothCommunicator
For use the library in a project you have to add jitpack.io to your root build.gradle (project):
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Then add the last version of BluetoothCommunicator to your app build.gradle
dependencies {
implementation 'com.github.niedev:BluetoothCommunicator:1.0.6'
}
To use this library add these permissions to your manifest:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Then add android:largeHeap="true" to the application tag in the manifest:
Example
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:name="com.bluetooth.communicatorexample.Global"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:largeHeap="true"
android:theme="#style/Theme.Speech">
<activity android:name="com.bluetooth.communicatorexample.MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Once you have downloaded the libray and set the manifest, you need to create a bluetooth communicator object, it is the object that handles all operations of bluetooth low energy library, if you want to manage the bluetooth connections in multiple activities I suggest you to save this object as an attribute of a custom class that extends Application and create a getter so you can access to bluetoothCommunicator from any activity or service with:
((custom class name) getApplication()).getBluetoothCommunicator();
Next step is to initialize bluetoothCommunicator, the parameters are: a context, the name by which the other devices will see us (limited to 18 characters and can be only characters listed in BluetoothTools.getSupportedUTFCharacters(context) because the number of bytes for advertising beacon is limited) and the strategy (for now the only supported stategy is BluetoothCommunicator.STRATEGY_P2P_WITH_RECONNECTION)
bluetoothCommunicator = new BluetoothCommunicator(this, "device name", BluetoothCommunicator.STRATEGY_P2P_WITH_RECONNECTION);
Then add the bluetooth communicator callback, the callback will listen for all events of bluetooth communicator:
bluetoothCommunicator.addCallback(new BluetoothCommunicator.Callback() {
#Override
public void onBluetoothLeNotSupported() {
super.onBluetoothLeNotSupported();
Notify that bluetooth low energy is not compatible with this device
}
#Override
public void onAdvertiseStarted() {
super.onAdvertiseStarted();
Notify that advertise has started, if you want to do something after the start of advertising do it here, because
after startAdvertise there is no guarantee that advertise is really started (it is delayed)
}
#Override
public void onDiscoveryStarted() {
super.onDiscoveryStarted();
Notify that discovery has started, if you want to do something after the start of discovery do it here, because
after startDiscovery there is no guarantee that discovery is really started (it is delayed)
}
#Override
public void onAdvertiseStopped() {
super.onAdvertiseStopped();
Notify that advertise has stopped, if you want to do something after the stop of advertising do it here, because
after stopAdvertising there is no guarantee that advertise is really stopped (it is delayed)
}
#Override
public void onDiscoveryStopped() {
super.onDiscoveryStopped();
Notify that discovery has stopped, if you want to do something after the stop of discovery do it here, because
after stopDiscovery there is no guarantee that discovery is really stopped (it is delayed)
}
#Override
public void onPeerFound(Peer peer) {
super.onPeerFound(peer);
Here for example you can save peer in a list or anywhere you want and when the user
choose a peer you can call bluetoothCommunicator.connect(peer founded) but if you want to
use a peer for connect you have to have peer updated (see onPeerUpdated or onPeerLost), if you use a
non updated peer the connection might fail
instead if you want to immediate connect where peer is found you can call bluetoothCommunicator.connect(peer) here
}
#Override
public void onPeerLost(Peer peer){
super.onPeerLost(peer);
It means that a peer is out of range or has interrupted the advertise,
here you can delete the peer lost from a eventual collection of founded peers
}
#Override
public void onPeerUpdated(Peer peer,Peer newPeer){
super.onPeerUpdated(peer,newPeer);
It means that a founded peer (or connected peer) has changed (name or address or other things),
if you have a collection of founded peers, you need to replace peer with newPeer if you want to connect successfully to that peer.
In case the peer updated is connected and you have saved connected peers you have to update the peer if you want to successfully
send a message or a disconnection request to that peer.
}
#Override
public void onConnectionRequest(Peer peer){
super.onConnectionRequest(peer);
It means you have received a connection request from another device (peer) (that have called connect)
for accept the connection request and start connection call bluetoothCommunicator.acceptConnection(peer);
for refusing call bluetoothCommunicator.rejectConnection(peer); (the peer must be the peer argument of onConnectionRequest)
}
#Override
public void onConnectionSuccess(Peer peer,int source){
super.onConnectionSuccess(peer,source);
This means that you have accepted the connection request using acceptConnection or the other
device has accepted your connection request and the connection is complete, from now on you
can send messages or data (or disconnection request) to this peer until onDisconnected
To send messages to all connected peers you need to create a message with a context, a header, represented by a single character string
(you can use a header to distinguish between different types of messages, or you can ignore it and use a random
character), the text of the message, or a series of bytes if you want to send any kind of data and the peer you want to send the message to
(must be connected to avoid errors), example: new Message(context,"a","hello world",peer);
If you want to send message to a specific peer you have to set the sender of the message with the corresponding peer.
To send disconnection request to connected peer you need to call bluetoothCommunicator.disconnect(peer);
}
#Override
public void onConnectionFailed(Peer peer,int errorCode){
super.onConnectionFailed(peer,errorCode);
This means that your connection request is rejected or has other problems,
to know the cause of the failure see errorCode (BluetoothCommunicator.CONNECTION_REJECTED
means rejected connection and BluetoothCommunicator.ERROR means generic error)
}
#Override
public void onConnectionLost(Peer peer){
super.onConnectionLost(peer);
This means that a connected peer has lost the connection with you and the library is trying
to restore it, in this case you can update the gui to notify this problem.
You can still send messages in this situation, all sent messages are put in a queue
and sent as soon as the connection is restored
}
#Override
public void onConnectionResumed(Peer peer){
super.onConnectionResumed(peer);
Means that connection lost is resumed successfully
}
#Override
public void onMessageReceived(Message message,int source){
super.onMessageReceived(message,source);
Means that you have received a message containing TEXT, for know the sender you can call message.getSender() that return
the peer that have sent the message, you can ignore source, it indicate only if you have received the message
as client or as server
}
#Override
public void onDataReceived(Message data,int source){
super.onDataReceived(data,source);
Means that you have received a message containing DATA, for know the sender you can call message.getSender() that return
the peer that have sent the message, you can ignore source, it indicate only if you have received the message
as client or as server
}
#Override
public void onDisconnected(Peer peer,int peersLeft){
super.onDisconnected(peer,peersLeft);
Means that the peer is disconnected, peersLeft indicate the number of connected peers remained
}
#Override
public void onDisconnectionFailed(){
super.onDisconnectionFailed();
Means that a disconnection is failed, super.onDisconnectionFailed will reactivate bluetooth for forcing disconnection
(however the disconnection will be notified in onDisconnection)
}
});
Finally you can start discovery and/or advertising:
bluetoothCommunicator.startAdvertising();
bluetoothCommunicator.startDiscovery();
All other actions that can be done are explained with the comments in the code of callback I wrote before.
To connect to the Device first you must perform you BLE scan which (if your using the starter code) runs a callback and add it to a list of found devices.
Add a filter in to only allow the set device you are looking for. As BLE advertises a packet upto 31 bytes you should have some data in here which discerns you device such as manufacturer id or data etc. Or if you are working on a simple project you can programmatically hard code in the device address.
Then when this device is discovered from the scan you can stop your BLE scan and automatically queue a connection request. This will ask for the GATT request to be made and therefore, grant you access to the GATT services and thus characteristics on the device.
You can add a view to your holder and set a click listener to it. A view could be a transparent rectangle all around your display card (or whatever you use).
I'd suggest this in depth read regarding BLE usage. On the click listener you can queue up the connection request.
I set up a very basic node.js server with the following code:
const express = require("express");
const app = express();
const port = 8000;
const ip = '192.16x.xxx.yyy'; // the x's and y's are placeholders. I didnt want to write my real IP here
const http = require("http").createServer();
const io = require("socket.io")(http);
// using the socket, we can send/receive data to/from the client
// the server listens for the "connection" event
io.on("connection", (socket) => {
// emit an event called "welcome" with a string as data to the client using its socket
// so, whenever the client connects to the server, it receives this welcome message
socket.emit("welcome", "Hello and Welcome to the Socket.io server.");
console.log("a new client is connected.")
});
// the server listens on the specified port for incoming events
http.listen(port, ip, () => {
console.log("Server is listening on " + ip + ":" + port);
});
So, the server is listening for the "connection" event and sends a welcome message when a client connects to the server.
The client which is an Android app looks like this:
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;
import com.github.nkzawa.emitter.Emitter;
import java.net.URISyntaxException;
public class MainActivity extends AppCompatActivity {
private Socket mSocket;
private TextView mTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.textView);
try {
/*
* IO.socket() returns a socket for the specified URL and port with the
* default options. Notice that the method caches the result, so you can
* always get a same Socket instance for an url from any Activity or
* Fragment.
* */
mSocket = IO.socket("http://192.16x.xxx.yyy:8000");
} catch (URISyntaxException e) {
e.getMessage();
}
/*
* We then can make the socket listen an event on onCreate lifecycle callback.
* */
mSocket.on("welcome", onNewMessage);
/*
* And we explicitly call "connect()" to establish the connection here.
* In this app, we use onCreate lifecycle callback for that, but it
* actually depends on your application.
* */
mSocket.connect();
}
private Emitter.Listener onNewMessage = new Emitter.Listener() {
#Override
public void call(final Object... args) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
String message = (String) args[0];
mTextView.setText("Received from Server: " + message);
}
});
}
};
/*
* Since an Android Activity has its own lifecycle, we should carefully manage the
* state of the socket also to avoid problems like memory leaks. In this app, we’ll
* close the socket connection and remove all listeners on onDestroy callback of
* Activity.
* */
#Override
protected void onDestroy() {
// socket gets disconnected
mSocket.disconnect();
// off() removes the listener of the "welcome" event
mSocket.off("welcome", onNewMessage);
super.onDestroy();
}
}
The client code is also very simple: It connects to the server and displays the message it gets from the server.
I got this ex. from the official Socket.IO website at https://socket.io/blog/native-socket-io-and-android/.
When I start the server with node server.js on the terminal of my laptop and use an Android emulator, then everything works fine.
But when I connect my real Android device to my laptop using a USB then the connection does not take place.
Why Android app (running within the real device) does not connect to the server ?
This worked for me: I created a socket with the IP 0.0.0.0, You should Change your IP at your node.js to this value.
Than i opened cmd and entered ipconfig to see what my ip adress is, this is your local IP adress. In the Code i am connecting to this IP Adress.
For example if you see IP 192.168.178.50 in your ipconfig you Change your Android Code to
mSocket = IO.socket("http://192.168.178.50:8000");
And your Node.js Code should look like
const ip = '0.0.0.0';
And dont Forget to Restart your Server when you changed the node.js code
I need a design patter to make a rabbitmq singleton connection that I can that will restart on connection loss/ internet provider switch.
My problem is that the connection is realised on another thread (asynctask) due to android main thread policy.
I use this connection for two services that consume and push.
public class RabbitSingletonConnection {
public static RabbitSingletonConnection instance;
private Connection connection;
private RabbitSingletonConnection() {
}
public static RabbitSingletonConnection getInstance() {
if (instance == null) {
instance = new RabbitSingletonConnection();
}
return instance;
}
public void Connect(RabbitMQConnectionCallback callback) {
if (connection != null && connection.isOpen()) {
LogUtil.hecsLog("RabbitSingletonConnection", "already connected");
callback.onConnect(connection);
return;
}
new RabbitConnectAsync(callback).execute("ip","user", "pass");
}
public void setConnection(Connection connection) {
this.connection = connection;
}
public Connection getConnection() {
return connection;
}
}
Problem is that this LogUtil.hecsLog("RabbitSingletonConnection", "already connected"); never occurs.
I have been digging and discovered something "Thread safe singleton using classloader declaration or ENUM approach", but this does not include a callback method.
EDIT
RabbitSingletonConnection.getInstance().Connect(new RabbitMQConnectionCallback() {
#Override
public void onConnect(Connection result) {
if (result != null && result.isOpen()){
RabbitSingletonConnection.getInstance().setConnection(result);
LogUtil.hecsLog(LOG, "Service has started");
}
}
});
This is a example Is it necessary to rebuild RabbitMQ connection each time a message is to be sent but I cannot make the connection in the constructor because it needs a thread to handle TimeoutException.
My goal is to start service after the rabbitMQ async task connects.
Do I need dependency injection?
I think I figured it out, I was starting/stopping the services on connectivity gain/loss. I looked at the facebook app and the services never stop, so the logic answer is to reset the workflow in the service not the service itslef.
I connect to the node server with socketio.SocketIO running as a service.And, When Service restarts,opens socket.io without socket.io closure.That's a problem.
A device making multiple socketIO connection on the server side.So the server is swelling..
! I am using gottox/socketio-java-client on android.
Check Socket is connected or not using socket.isConnected().
This will return true if socket is connected
Its just an idea so i don't know the limitations. pls let me know.
You can ping the server to check if the connection is alive.
In android
socket.emit("ping","ping");
socket.on("pong",pingHandler); //EmitterListener
private Emitter.Listener pingHandler=new Emitter.Listener(){
#Override
public void call(final Object... args) {
Log.d("SocketStatus","Connection is active");
});
}
and make the server return response for the ping
socket.on("ping",function(data){
socket.emit("pong","pong"); //from your server ex.Node.js
});
You can check the socket.connected property:
var socket = io.connect();
console.log('Connected status before onConnect', socket.socket.connected);
socket.on('connect', function() {
console.log('Connected status onConnect', socket.socket.connected);
});
It's updated dynamically, if the connection is lost it'll be set to false until the client picks up the connection again. So easy to check for with setInterval or something like that.
Another solution would be to catch disconnect events and track the status yourself.
The following is an expansion/modification of Rafique Mohammed answer above. The correct way is to try to reconnect on client side.
Internet drops (server cannot communicate disconnection to client). Server crashes (server may/may not be able to tell client. Server Restart (server can tell but that just extra work). After reconnection you will also like to rejoin the room for seamless communication
public void connectAfterDisconnectSocket(String senderActivity) {
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
boolean isConnected = false;
isConnected = mSocket != null && mSocket.connected();
if (!isConnected) {
SocketIOClient socketIOClient = new SocketIOClient();
socketIOClient.connectToSocketIO();
if (senderActivity.equals("A")) {
A.joinChatRoom(room);
}
if (senderActivity.equals("B")) {
B.joinChatRoom(room);
}
}
}
}, 0, 1000); //put here time 1000 milliseconds=1 second
}
I am trying to connect a bluetooth headset to my android device using the android developer page as a reference. http://developer.android.com/guide/topics/connectivity/bluetooth.html
My problem is when i trying calling the getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET) method, I am unsure of what to pass for context? I located this error from the question here:
can not connect to bluetooth headset in android
I am extremely new to this so I will apologize in advance if this is a silly question. I have spent a lot of time trying to research this but every example and documentation I find just has a context variable passed in so I am not sure where I am going wrong. My code, which is more or less a copy from the android documentation is:
// Establish connection to the proxy.
boolean mProfileProxy = mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
Log.d(TAGP,"Get Adapter Success: "+mProfileProxy);
Log.d(TAGP,"Context: "+context);
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
Log.d(TAGP,"BLuetooth Headset: "+mBluetoothHeadset);
Log.d(TAGP,"Proxy: "+proxy);
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
The context can be an activity or service context. So if the code above is in a class that extends Activity or Service you can pass this.
You can use my answer at Using the Android RecognizerIntent with a bluetooth headset