diff --git a/.idea/modules.xml b/.idea/modules.xml
index 8efa824..1a0686b 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,8 +2,10 @@
+
-
+
+
\ No newline at end of file
diff --git a/ardutooth/build.gradle b/ardutooth/build.gradle
index 1507d62..4a3b5a3 100644
--- a/ardutooth/build.gradle
+++ b/ardutooth/build.gradle
@@ -1,11 +1,11 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 25
- buildToolsVersion "25.0.3"
+ compileSdkVersion 29
+ buildToolsVersion '29.0.2'
defaultConfig {
minSdkVersion 14
- targetSdkVersion 25
+ targetSdkVersion 29
versionCode 2
versionName "2.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -19,10 +19,11 @@ android {
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ api fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
- compile 'com.android.support:appcompat-v7:25.3.1'
- testCompile 'junit:junit:4.12'
+ //noinspection GradleCompatible
+ api 'com.android.support:appcompat-v7:25.3.1'
+ testImplementation 'junit:junit:4.13'
}
diff --git a/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/Ardutooth.java b/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/Ardutooth.java
index d4232e7..d2d856a 100644
--- a/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/Ardutooth.java
+++ b/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/Ardutooth.java
@@ -3,14 +3,19 @@
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
import android.util.Log;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.util.UUID;
/**
* This singleton class represents the main component of the library.
- * It can be used to easily set a stable connection with an Arduino and to send data to it using the Serial Monitor.
+ * It can be used to easily set a stable connection with an Arduino and to send/receive data to/from it using the Serial Monitor.
*
* The first thing you need is to create or get an instance of {@link Ardutooth} using something like
* {@code Ardutooth mArdutooth = Ardutooth.getInstance(this)} where parameter represents the instance of
@@ -65,7 +70,7 @@ public boolean isConnected() {
} catch (Exception e) {
Log.d(Ardutooth.TAG, "An error occurred while retrieving connection", e);
}
- return mBtHandler.connected;
+ return BluetoothHandler.connected;
}
/**
@@ -206,4 +211,38 @@ public void sendBoolean(boolean value) {
e.printStackTrace();
}
}
+
+ /**
+ * Reads a single character, as defined by {@link BufferedReader}'s readLine method,
+ * from the Arduino, casts it into a {@link char}, and returns it.
+ *
+ * @return char value read
+ */
+ public char receiveChar(){
+ char c = 0;
+ if (mBtHandler.getSocket() != null)
+ try {
+ c = (char) mBtHandler.getInputReader().read();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return c;
+ }
+
+ /**
+ * Reads a line, as defined by {@link BufferedReader}'s readLine method, from the Arduino.
+ *
+ * @return {@link String} line read
+ */
+ public String receiveLine(){
+ String result = "";
+ if (mBtHandler.getSocket() != null)
+ try {
+ result = mBtHandler.getInputReader().readLine();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ return result;
+ }
+
}
\ No newline at end of file
diff --git a/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/BluetoothHandler.java b/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/BluetoothHandler.java
index 68f9c26..45f90f5 100644
--- a/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/BluetoothHandler.java
+++ b/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/BluetoothHandler.java
@@ -1,5 +1,6 @@
package io.github.giuseppebrb.ardutooth;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -15,9 +16,14 @@
import android.util.Log;
import android.widget.Toast;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Set;
/**
* This singleton class handles the bluetooth connection with Arduino.
@@ -37,6 +43,7 @@ class BluetoothHandler {
private Activity mActivity;
private OutputStream mOutStream;
+ private BufferedReader mReader;
/**
* Constructor
@@ -59,7 +66,7 @@ private BluetoothHandler(Activity activity) {
}
/**
- * Allows to create or get the unique instance of the compononent.
+ * Allows to create or get the unique instance of the component.
*
* @param activity Define the {@link android.content.Context} where the object will work.
* @return
@@ -80,21 +87,28 @@ protected void createConnection() {
/**
* Open the input and output communication with Arduino.
+ *
+ * @return true if communication has been set up successfully, else false
*/
- private void connect() {
+ @SuppressLint("NewApi")
+ private boolean connect() {
OutputStream tmpOut = null;
+ InputStream tmpIn = null;
try {
mSocket.connect();
try {
tmpOut = mSocket.getOutputStream();
+ tmpIn = mSocket.getInputStream();
} catch (IOException e) {
Log.e(Ardutooth.TAG, "Error occurred when creating output stream", e);
}
mOutStream = tmpOut;
+ mReader = new BufferedReader(new InputStreamReader(tmpIn, StandardCharsets.US_ASCII));
} catch (IOException e) {
Log.e(Ardutooth.TAG, "Error opening connection", e);
closeConnection();
}
+ return mSocket.isConnected();
}
/**
@@ -105,6 +119,7 @@ protected void closeConnection() {
try {
mSocket.close();
mOutStream.close();
+ mReader.close();
} catch (IOException e) {
Log.e(Ardutooth.TAG, "Error while closing socket", e);
Toast.makeText(mActivity.getApplication(), mActivity.getString(R.string.error_occurred_disconnecting), Toast.LENGTH_LONG).show();
@@ -118,12 +133,16 @@ protected void closeConnection() {
* Retrieve a bluetooth connection if there's one already established.
*/
protected void retrieveConnection() {
+
+ Log.d(Ardutooth.TAG, "Attempting to retrieve existing connection");
if (mAdapter.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED) {
List devices = mBluetoothHeadset.getConnectedDevices();
mBtDevice = devices.get(0);
connected = true;
Log.d(Ardutooth.TAG, "Already connected with a device");
- }
+ } else
+ Log.d(Ardutooth.TAG, "Not connected with a device");
+
}
/**
@@ -137,7 +156,9 @@ private boolean isBluetoothSupported() {
/**
* Check if bluetooth is on, if no ask the user to turn it on.
- * If bluetooth is on but there's no connection yet ask the user to open bluetooth settings and to connect with the Arduino bluetooth module.
+ * If bluetooth is on but there's no connection yet, create a list of paired devices and ask
+ * the user to pick one to connect with. If there are no paired devices, open bluetooth settings
+ * and pair and connect with the Arduino bluetooth module.
*/
private void checkBluetoothState() {
if (isBluetoothSupported()) {
@@ -164,24 +185,19 @@ public void onClick(DialogInterface dialog, int which) {
} else {
Log.d(Ardutooth.TAG, "Bluetooth is already on");
retrieveConnection();
- if (connected == false) {
- builder.setTitle(mActivity.getString(R.string.bluetooth_not_connected));
- builder.setMessage(mActivity.getString(R.string.bluetooth_not_connected_message));
- builder.setCancelable(true);
- builder.setPositiveButton(mActivity.getString(R.string.open_settings), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent settings_intent = new Intent(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
- mActivity.startActivityForResult(settings_intent, REQUEST_ENABLE_BT);
- }
- });
- builder.setNegativeButton(mActivity.getString(R.string.cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- });
- builder.create().show();
+ if (!connected) {
+
+ if (mAdapter.getBondedDevices().size() != 0) {
+
+ promptUserToConnectWithAPairedDevice();
+
+ } else{
+
+ Log.d(Ardutooth.TAG, "There are no bonded devices");
+
+ promptUserToPairWithADevice();
+ }
+
} else {
Toast.makeText(mActivity.getApplication(), mActivity.getString(R.string.already_connected) + mBtDevice.getName(), Toast.LENGTH_SHORT).show();
}
@@ -199,8 +215,118 @@ public void onClick(DialogInterface dialog, int which) {
}
}
+ /**
+ * Creates a list of Bonded (paired) Bluetooth devices and displays their names in a dialog.
+ * The user can select a previously paired device to connect with, or pair and connect with
+ * a new one.
+ */
+ private void promptUserToConnectWithAPairedDevice() {
+
+ Set pairedDevicesSet = mAdapter.getBondedDevices();
+
+ //the names are what is displayed in the dialog list
+ String[] pairedDevicesNames = new String[pairedDevicesSet.size()];
+
+ //This is the list of paired BluetoothDevice object references.
+ // It is final so that it can be accessed in the anonymous inner-class EventHandler.
+ final BluetoothDevice[] pairedDevicesArray = new BluetoothDevice[pairedDevicesSet.size()];
+
+ //Create a key-value association between the list of names and the list of devices.
+ int i = 0;
+ for (BluetoothDevice device : pairedDevicesSet) {
+ pairedDevicesNames[i] = device.getName();
+ Log.d(Ardutooth.TAG, "Found bonded Device: " + pairedDevicesNames[i]);
+ pairedDevicesArray[i] = device;
+ i++;
+ }
+
+ //This is a weird "hack" I resorted to. It allows the string to change,
+ // while still being able to reference it in the PositiveButton event handler.
+ final String[] selectedDeviceAddress = {""};
+
+ builder.setTitle(mActivity.getString(R.string.bluetooth_not_connected));
+ builder.setCancelable(true);
+ builder.setSingleChoiceItems(pairedDevicesNames, -1,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ selectedDeviceAddress[0] = pairedDevicesArray[which].getAddress();
+ Log.d(Ardutooth.TAG, "user selected " + pairedDevicesArray[which].getName());
+ }
+ });
+ builder.setPositiveButton(mActivity.getString(R.string.confirm), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mBtDevice = mAdapter.getRemoteDevice(selectedDeviceAddress[0]);
+ Log.d(Ardutooth.TAG, "mBtDevice = " + mBtDevice.getName() + ", " + mBtDevice.getAddress());
+ openRFCOMMSocketWithBondedDevice();
+ }
+ });
+ builder.setNeutralButton(mActivity.getString(R.string.pair_with_new_device), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent settings_intent = new Intent(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
+ mActivity.startActivityForResult(settings_intent, REQUEST_ENABLE_BT);
+ }
+ });
+ builder.setNegativeButton(mActivity.getString(R.string.cancel), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ builder.show();
+ }
+
+ /**
+ * If the device has already been bonded, no action will be issued to the BroadcastReceiver
+ * until a connection is made (at which point the BluetoothDevice.ACTION_ACL_CONNECTED will be
+ * issued). Because of this the BroadcastReceiver will not initiate creating a socket, so we
+ * must do that manually here.
+ */
+ private void openRFCOMMSocketWithBondedDevice() {
+ try {
+ int counter = 0;
+ do {
+ mSocket = mBtDevice.createRfcommSocketToServiceRecord(Ardutooth.UUID);
+ counter++;
+ }
+ while (!connect() && counter != 2);
+ Log.d(Ardutooth.TAG, "Connected to " + mBtDevice.getName() + ": " + mSocket.isConnected());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * If Bluetooth is already on, and not already connected, and there are no Devices already
+ * bonded (paired), prompt the user to pair with a device in Android's Bluetooth settings.
+ */
+ private void promptUserToPairWithADevice() {
+ Log.d(Ardutooth.TAG, "prompting user to pair with a device");
+
+ builder.setTitle(mActivity.getString(R.string.bluetooth_not_connected));
+ builder.setMessage(mActivity.getString(R.string.pair_with_another_device_message));
+ builder.setCancelable(true);
+ builder.setPositiveButton(mActivity.getString(R.string.open_settings), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent settings_intent = new Intent(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
+ mActivity.startActivityForResult(settings_intent, REQUEST_ENABLE_BT);
+ }
+ });
+ builder.setNegativeButton(mActivity.getString(R.string.cancel), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ builder.create().show();
+ }
+
/**
* Retrieve the device connected with.
+ *
* @return the connected with.
*/
protected BluetoothDevice getDeviceConnected() {
@@ -208,7 +334,8 @@ protected BluetoothDevice getDeviceConnected() {
}
/**
- * Retrieve the socket where's the connection it's happaning.
+ * Retrieve the socket where's the connection it's happening.
+ *
* @return the socket opened for the connection.
*/
protected BluetoothSocket getSocket() {
@@ -217,24 +344,36 @@ protected BluetoothSocket getSocket() {
/**
* Retrieve the {@link OutputStream} of the communication
+ *
* @return the {@link OutputStream} of the communication
*/
protected OutputStream getOutputStream() {
return mOutStream;
}
+ /**
+ * Retrieve the {@link BufferedReader} which reads the {@link InputStream} of the communication
+ *
+ * @return the {@link BufferedReader} which reads the {@link InputStream} of the communication
+ */
+ protected BufferedReader getInputReader() {
+ return mReader;
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
- Log.d(Ardutooth.TAG, "Connected");
+ Log.d(Ardutooth.TAG, "Connected from broadcast receiver");
mBtDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Toast.makeText(mActivity.getApplication(), mActivity.getString(R.string.connected_to) + mBtDevice.getName(), Toast.LENGTH_SHORT).show();
connected = true;
try {
- mSocket = mBtDevice.createRfcommSocketToServiceRecord(Ardutooth.UUID);
- connect();
+ if (!mSocket.isConnected()) { //might already be connected from openRFCOMMSocketWithBondedDevice()
+ mSocket = mBtDevice.createRfcommSocketToServiceRecord(Ardutooth.UUID);
+ connect();
+ }
} catch (IOException e) {
Log.e(Ardutooth.TAG, "Error during socket creation", e);
}
@@ -267,4 +406,5 @@ public void onServiceDisconnected(int profile) {
}
}
};
+
}
\ No newline at end of file
diff --git a/ardutooth/src/main/res/values/strings.xml b/ardutooth/src/main/res/values/strings.xml
index 8979a52..6acdedb 100644
--- a/ardutooth/src/main/res/values/strings.xml
+++ b/ardutooth/src/main/res/values/strings.xml
@@ -1,11 +1,12 @@
Already connected with\u0020
Bluetooth is not connected
- Open settings to connect via bluetooth with another device.
+ Open settings to pair via bluetooth with another device.
Bluetooth not supported
This device doesn\'t support bluetooth. The requested operation cannot be done.
Bluetooth is off
Cancel
+ Confirm
Cannot disconnect because there\'s no connection established
Connected to\u0020
Disconnected from\u0020
@@ -13,4 +14,5 @@
Ardutooth
Open settings
Turn on bluetooth in settings and connect to another device.
+ Connect Other
diff --git a/build.gradle b/build.gradle
index c2eea8e..b27b75b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,9 +3,10 @@
buildscript {
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.3'
+ classpath 'com.android.tools.build:gradle:3.6.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -15,6 +16,7 @@ buildscript {
allprojects {
repositories {
jcenter()
+ google()
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index c319b07..59fdcda 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Jun 09 19:31:04 CEST 2017
+#Tue May 12 12:13:42 EDT 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip