Android bluetooth device in background not as keyboard - java

I have barcode scanner who should work with my app. It is supposed to run in the background and my app is responding to input coming from the scanner in its own way. However it should not be used as a hardware keyboard and its input not directed to the firstresponder (no text entries i.e.). Currently i'm stuck with it although i know i have done a solution like described before (couple years ago for a company).
The first part of the question is now how to prevent the bluetooth device from being attached as a hardware keyboard? Can i control this behaviour or is it maybe some mode or setting that the bluetooth device must support (if yes is there name for it to check in the specifications)?
I guess if the device is not attached as a bluetooth keyboard i could establish a connection and listen to the bluetooth socket input stream for available bytes and collect them. Currently i cannot establish a connection because
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
socket = device.createRfcommSocketToServiceRecord(uuid);
socket.connect();
throws an java.io.IOException with message read failed, socket might closed or timeout, read ret: -1. Any ideas why i cannot connect to the socket, although the device is currently paired to my device?
EDIT:
Because it was asked, the complete connection source code
private void initBluetooth() {
BTAdapter = BluetoothAdapter.getDefaultAdapter();
if (BTAdapter == null) {
Log.e(getClass().getName(), "no BT Adapter");
return;
}
if (!BTAdapter.isEnabled()) {
Intent enableBT = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBT, REQUEST_BLUETOOTH);
return;
}
listDevices();
}
private void listDevices() {
Set<BluetoothDevice> pairedDevices = BTAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
Log.d(getClass().getName(), String.format("Device found %s", device.getName()));
if(device.getName().indexOf("Barcode") > 0) {
mmDevice = device;
try {
openBT();
} catch(IOException e) {
Log.e(getClass().getName(), "Failed", e);
}
break;
}
}
}
}
void openBT() throws IOException
{
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
//now make the socket connection in separate thread
Thread connectionThread = new Thread(new Runnable() {
#Override
public void run() {
// Always cancel discovery because it will slow down a connection
BTAdapter.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) {
//connection to device failed so close the socket
e.printStackTrace();
try {
mmSocket.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
});
connectionThread.start();
mmInputStream = mmSocket.getInputStream();
}

Related

Trouble sending data from Android to Arduino via Bluetooth

I'm trying to set up a system where an android app connects to Arduino via Bluetooth and tells it to either turn on or off its LED. I've looked through a lot of pages and source code and saw many people did it as I did but somehow my code isn't working and I cannot determine why.
Here's the entirety of my Arduino code, really simple and short.
#include <SoftwareSerial.h>
SoftwareSerial Blue(0,1); // rx tx
int LED = 13; // Led connected
char data;
char state = 0;
void setup()
{
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
Serial.begin(9600);
Blue.begin(9600);
}
void loop()
{
while(Blue.available()==0);
if(Blue.available()>0){ // read from android via bluetooth
data = Blue.read();
Serial.println(data);
}
if (data == '1') // If data is 1, turn ON the LED
{
digitalWrite(LED,HIGH);
Serial.println("LED ON ");
}
if( data == '2') // if data is 2, turn OFF the LED
{
digitalWrite(LED,LOW);
Serial.println("LED OFF");
}
}
And here's a snippet of my android code that sends data to Arduino to control LED
switchLight.setOnClickListener(new View.OnClickListener() { // button that will switch LED on and off
#Override
public void onClick(View v) {
Log.i("[BLUETOOTH]", "Attempting to send data");
if (mmSocket.isConnected() && btt != null) { //if we have connection to the bluetoothmodule
if (!lightflag) {
try{
mmSocket.getOutputStream().write("1".toString().getBytes());
showToast("on");
}catch (IOException e) {
showToast("Error");
// TODO Auto-generated catch block
e.printStackTrace();
}
//btt.write(sendtxt.getBytes());
lightflag = true;
} else {
try{
mmSocket.getOutputStream().write("2".toString().getBytes());
showToast("off");
}catch (IOException e) {
showToast("Error");
// TODO Auto-generated catch block
e.printStackTrace();
}
//btt.write(sendtxt.getBytes());
lightflag = false;
}
}
else {
Toast.makeText(MainActivity.this, "Something went wrong", Toast.LENGTH_LONG).show();
}
}
});
This is the part of the code that connects to Arduino Bluetooth module. Again, fairly simple stuff and its only purpose are to connect to the module.
BluetoothAdapter bta; //bluetooth stuff
BluetoothSocket mmSocket; //bluetooth stuff
BluetoothDevice mmDevice; //bluetooth stuff
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("[BLUETOOTH]", "Creating listeners");
final TextView response = findViewById(R.id.response);
Button switchLight = findViewById(R.id.switchlight);
Button connectBT = findViewById(R.id.connectBT);
connectBT.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BluetoothSocket tmp = null;
mmDevice = bta.getRemoteDevice(MODULE_MAC);
Log.i("[BLUETOOTH]", "Attempting to send data");
try {
tmp = mmDevice.createRfcommSocketToServiceRecord(MY_UUID);
mmSocket = tmp;
mmSocket.connect();
Log.i("[BLUETOOTH]","Connected to: "+mmDevice.getName());
showToast("Connected to: " + mmDevice.getName());
}catch(IOException e){
try {mmSocket.close();
}catch(IOException c){return;}
}
}
});
When I connect my android to the Arduino and track the serial monitor on Arduino IDE, instead of reading either 1 or 2, it reads something that looks like this:
This is produced using the Serial. println function in my Arduino code and I'm pretty sure it should display 1 or 2 but as you can see it does not. I've tried multiple workarounds like declaring it as int or char etc. If you can pinpoint any issue I'd much appreciate it.

In android studio, how to prevent errors with buffered reader reading from a url, when the phone has no internet

In my app, when I press a button, a buffered reader should read a line of a text from a text file online.
As a test, if the text is read correctly, I want a toast to appear saying "success". If the read fails, such as because the phone has no connection to the internet, I want a toast to appear saying "failed".
However, if I turn on airplane mode, and then press the button, it simply seems to "hang" forever, and the "failed" toast never appears -- or it just crashes the app entirely.
This is the code I am using:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new NotePadFileFromServer().execute();
}
});
public class NotePadFileFromServer extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
try {
url = new URL(TextFileURL);
bufferReader = new BufferedReader(new InputStreamReader(url.openStream()));
TextHolder = bufferReader.readLine();
bufferReader.close();
} catch (Exception e) {
Toast.makeText(MainActivity.this, "Fail!", Toast.LENGTH_SHORT).show();
}
return null;
}
#Override
protected void onPostExecute(Void finalTextHolder) {
Toast.makeText(MainActivity.this, "Success!", Toast.LENGTH_SHORT).show();
super.onPostExecute(finalTextHolder);
}
}
I tried adding in a pre-check using ConnectivityManager to test if there is an internet connection as per this code: https://stackoverflow.com/a/58146646/4250107, but that only works if the phone user has specifically turned off the internet, and the crashes occur again if the wifi function is turned on, but there is no internet. I then tried checking the internet connection, as per this code: https://stackoverflow.com/a/58146896/4250107, but this also crashes the app, as apparently (?) attempting to ping a server does not work on Samsung phones.
EDIT: Final fixed code.
public class NotePadFileFromServer extends AsyncTask<Void, Void, String>{
#Override
protected String doInBackground(Void... params) {
try {
URLConnection url = new URL(TextFileURL).openConnection());
url.setConnectTimeout(1000);
url.setReadTimeout(1000);
bufferReader = new BufferedReader(new InputStreamReader(url.getInputStream()));
TextHolder = bufferReader.readLine();
bufferReader.close();
return "Success!";
} catch (Exception e) {
return "Fail!";
}
}
#Override
protected void onPostExecute(String success) {
Toast.makeText(MainActivity.this, success, Toast.LENGTH_SHORT).show();
super.onPostExecute(success);
}
}
The app is crashing because you are trying to perform UI related task in the Background Thread when there is an exception. So, the following is responsible for the crash,
catch (Exception e) {
Toast.makeText(MainActivity.this, "Fail!", Toast.LENGTH_SHORT).show();
}
So, you can avoid the crash by refactoring you code in the following way,
public class NotePadFileFromServer extends AsyncTask<Void, Void, String>{
#Override
protected String doInBackground(Void... params) {
try {
url = new URL(TextFileURL);
bufferReader = new BufferedReader(new InputStreamReader(url.openStream()));
TextHolder = bufferReader.readLine();
bufferReader.close();
return "Success!";
} catch (Exception e) {
return "Fail!";
}
}
#Override
protected void onPostExecute(String finalTextHolder) {
Toast.makeText(MainActivity.this, finalTextHolder, Toast.LENGTH_SHORT).show();
super.onPostExecute(finalTextHolder);
}
}
And in case of timeout issue which you described here as hang, I would recommend you to use openConnection() (which returns a UrlConnection) instead of openStream(). So that you can set shorter connection and read timeout.
Yes, as you say ConnectivityManager will not help you because if you have wifi but no internet it will crash.
However, it is possible to check internet connection. I couldn't do it with ping (same as you), but i could when i try to open a socket to some of the opened ports (80 or 443). Here is a code using rxjava but you can adapt it to what you are using.
fun isOnline(context: Context?): Single<Boolean> {
return Single.fromCallable {
try {
// Connect to Google DNS to check for connection
val timeoutMs = 2500
val socket = Socket()
val address = InetAddress.getByName("www.google.com")
val socketAddress = InetSocketAddress(address, 443)
socket.connect(socketAddress, timeoutMs)
socket.close()
true
} catch (e: Exception) {
false
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
In my case i opened the socket with my backend so also i can check if it is working. I put www.google.com in case you don't have a backend.
The way to use it is:
isOnline(context).subscribe { hasInternet ->
//Conditional check
}

android Java - Textview not appending text when activity restarts

I've been trying to create a function in my app that consist in a bluetooth RFID scanner, it's paired to my device and I have it working and all.
I can receive the text and log it in the console, when I compile the activity, everything goes fine, the stick reads the code, and then appends the text into an EditText, but if I go back and enter the activity again, I can see the code in the log, but the text doesn't go to the Edittext.
I tried a lot of different approaches, but nothing seems to work :/
here's the code I have:
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bluetooth);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> bondedSet = mBluetoothAdapter.getBondedDevices();
if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth is not available.", Toast.LENGTH_LONG).show();
}
if (!mBluetoothAdapter.isEnabled()) {
Toast.makeText(this, "Please enable your BT and re-run this program.", Toast.LENGTH_LONG).show();
finish();
}
if (mBluetoothAdapter.isEnabled()) {
if(bondedSet.size() == 1){
for(BluetoothDevice device : bondedSet){
address = device.getAddress();
Log.d("bt:", address);
}
}
}
String address = "00:A0:96:2A:0A:1B";
out = (EditText) findViewById(R.id.output);
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
Log.d(TAG, device.getName() + " connected");
myConnection = new ConnectThread(device);
myConnection.start();
}
private class ConnectThread extends Thread {
private final BluetoothSocket mySocket;
Message msg;
public ConnectThread(BluetoothDevice device) {
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
Log.d(TAG, "CONNECTION IN THREAD DIDNT WORK");
}
mySocket = tmp;
}
Handler uiThreadHandler = new Handler() {
public void handleMessage(Message msg) {
out = (EditText) findViewById(R.id.output);
Object o = msg.obj;
out.append(o.toString().trim());
Log.d("handler", o.toString());
}
};
public void run() {
out = (EditText) findViewById(R.id.output);
Log.d(TAG, "STARTING TO CONNECT THE SOCKET");
setName("My Connection Thread");
InputStream inStream = null;
boolean run = false;
mBluetoothAdapter.cancelDiscovery();
try {
mySocket.connect();
run = true;
} catch (IOException e) {
Log.d(TAG, this.getName() + ": CONN DIDNT WORK, Try closing socket");
try {
mySocket.close();
Log.d(TAG, this.getName() + ": CLOSED SOCKET");
} catch (IOException e1) {
Log.d(TAG, this.getName() + ": COULD CLOSE SOCKET", e1);
this.destroy();
}
run = false;
}
synchronized (BluetoothActivity.this) {
myConnection = null;
}
byte[] buffer = new byte[1024];
int bytes;
// handle Connection
try {
inStream = mySocket.getInputStream();
while (run) {
try {
bytes = inStream.read(buffer);
readMessage = new String(buffer, 0, bytes);
msg = uiThreadHandler.obtainMessage();
msg.obj = readMessage;
uiThreadHandler.sendMessage(msg);
Log.d(TAG, "Received: " + readMessage);
} catch (IOException e3) {
Log.d(TAG, "disconnected");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
My guess is that this has something to do with the Thread itself. When you start your Activity for the first time, you also call .start() on the Thread, that would work fine.
The problem is when you leave your Activity and open it up again. In that case, one of onStop() or onPause() is called (depending on situation), and onRestart() or onResume() will be called afterwards respectively.
The trick comes now: Meanwhile all that process, your Thread is still running. As you show your code, it has not been stopped/paused, and keeps running all the time. So basically my tip is that there's something you do within your onCreate() method of your Activity that should also be done in your onPause() and onStop() events, and my another tip it's somewhere within your ConnectThread(BluetoothDevice device) method.
To know how to procceed, I'd firstly define both onStop() and onPause() methods within your Activity and see which is fired, log every attribute to see its value/state, and that way you'll be able to debug what is failing.
There's a diagram of the Activity lifecycle.
Problem was solved, the code works, and the TextView get the inputstream, the problem was when i left the activity, the thread continued to work, so far, no problem at all, after TONS of hours spent on this, i turn the TextView a static var and it worked :)
If anyone reads this, i hope it helps.

After long wait time bluetooth socket state mixed ups

I'm currently trying to fix a problem I encountered with my Android app which require a Bluetooth connection. For a moment everything seem to work right, But i noticed something strange when the slave Bluetooth device, I want to connect with, is not powered on. Here is my code :
private void connectDevice() {
mBluetoothAdapter.cancelDiscovery();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
errorExit("Fatal Error", "Socket create failed: " + e.getMessage() + ".");
}
//Try to establish the connection. This will block until it connects.
Log.d(TAG, "...Connecting...");
try {
btSocket.connect();
Log.d(TAG, "....Connection ok...");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "Unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
//Create a data stream so we can talk to server.
Log.d(TAG, "...Create Socket...");
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
mActionBar.setSubtitle("Connected");
return;
}
And here is where I call this method :
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(requestCode){
case REQUEST_ENABLE_BT:
if (resultCode == RESULT_OK) {
Toast.makeText(this, R.string.bt_enabled, Toast.LENGTH_SHORT).show();
setupCom();
break;
}
else {
// User did not enable Bluetooth or an error occurred
if(D) Log.d(TAG, "BT not enabled");
Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
finish();
break;
}
case REQUEST_CONNECT_DEVICE:
if (resultCode == RESULT_OK){
retrieveAddresse(data);
connectDevice();
}
break;
}
return;
}
My problem is that, when I'm not in range or the device I want to connect to is not powered on, the connectDevice() method seem to execute all the code even if it's not possible to connect because Android OS don't want to be blocked by the connection process. I noticed this problem because mActionBar.setSubtitle("Connected"); get executed and because when I try to reconnect when I'm in range or the slave bluetooth module is ON. I can't connect to it unless I relaunch my application.
Put these lines :
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
mActionBar.setSubtitle("Connecté");
inside the first try. In this way they will be executed only if the devices establish a connection. As actually they are outside try/catch, they will always executed, even without connection.

Pair device in bluetooth android

I am trying to connect another android device by Bluetooth, So first I paired the devices and then I tried sending the request for another device.
When I called the system bluetooth settings screen, I am able to pair the another device
Intent btSettingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
startActivityForResult(btSettingsIntent, Pair_Request);
When I tried to pair by programmaticaly, I'm getting this dialogue and entered pair digit in my device but no response in another device
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(strAddress);
Intent intent = new Intent("android.bluetooth.device.action.PAIRING_REQUEST");
intent.putExtra("android.bluetooth.device.extra.DEVICE", device);
intent.putExtra("android.bluetooth.device.extra.PAIRING_VARIANT", 0);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
I got this image in device, when I type pair number, I'm not getting anything in another deivce
if the device is already paired , then you can use
if(device.getBondState()==device.BOND_BONDED){
Log.d(TAG,device.getName());
//BluetoothSocket mSocket=null;
try {
mSocket = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e1) {
// TODO Auto-generated catch block
Log.d(TAG,"socket not created");
e1.printStackTrace();
}
try{
mSocket.connect();
}
catch(IOException e){
try {
mSocket.close();
Log.d(TAG,"Cannot connect");
} catch (IOException e1) {
Log.d(TAG,"Socket not closed");
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
for the MY_UUID use
private static final UUID MY_UUID = UUID.fromString("0000110E-0000-1000-8000-00805F9B34FB");
the above code snippet is just to connect your device to an A2DP supported device.
I hope it will work. tell me if not.

Categories