From b6179f8d0811641a306f7eb86b4132f79bc83aa2 Mon Sep 17 00:00:00 2001 From: grabinow2 Date: Tue, 12 May 2020 12:43:51 -0400 Subject: [PATCH 1/6] Updated project to later SDK, other dependency updates, etc. --- .idea/codeStyles/Project.xml | 116 ++++++++++++++++++ .idea/gradle.xml | 4 +- .idea/misc.xml | 34 ++--- .idea/modules.xml | 4 +- ardutooth/build.gradle | 15 +-- .../ardutooth/BluetoothHandler.java | 2 +- build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 8 files changed, 154 insertions(+), 29 deletions(-) create mode 100644 .idea/codeStyles/Project.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..681f41a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 5cd0919..b2bdf80 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,14 +1,16 @@ + From dbf6440b528d430a760d37561c1b3048fed19d7f Mon Sep 17 00:00:00 2001 From: grabinow2 Date: Tue, 12 May 2020 20:55:18 -0400 Subject: [PATCH 4/6] Added receiving capability to Ardutooth. Added a reference to the InputStream returned from BluetoothHandler.msocket.getInputStream() method. Used that reference to Receive data from Arduino using InputStream.read() method. --- .../giuseppebrb/ardutooth/Ardutooth.java | 22 +++++++++++-------- .../ardutooth/BluetoothHandler.java | 18 +++++++++++---- 2 files changed, 27 insertions(+), 13 deletions(-) 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 f317f6d..796605a 100644 --- a/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/Ardutooth.java +++ b/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/Ardutooth.java @@ -3,9 +3,14 @@ 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; /** @@ -212,11 +217,12 @@ public void sendBoolean(boolean value) { * * @return {@link char} value read */ + @RequiresApi(api = Build.VERSION_CODES.KITKAT) public char receiveChar(){ char c = 0; if (mBtHandler.getSocket() != null) try { - c = (char) mBtHandler.getInputStream().read(); + c = (char) mBtHandler.getInputReader().read(); } catch (IOException e) { e.printStackTrace(); } @@ -228,18 +234,16 @@ public char receiveChar(){ * * @return {@link String} line read */ + @RequiresApi(api = Build.VERSION_CODES.KITKAT) public String receiveLn(){ - - StringBuilder str = new StringBuilder(); + String result = ""; if (mBtHandler.getSocket() != null) try { - do{ - str.append((char) mBtHandler.getInputStream().read()); - }while (mBtHandler.getSocket() != null && str.indexOf("\n") == -1); - } catch (IOException e) { - e.printStackTrace(); + result = mBtHandler.getInputReader().readLine(); + } catch (IOException ex) { + ex.printStackTrace(); } - return str.toString(); + 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 dba179b..c991cbe 100644 --- a/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/BluetoothHandler.java +++ b/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/BluetoothHandler.java @@ -11,13 +11,18 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.os.Build; +import android.support.annotation.RequiresApi; import android.support.v7.app.AlertDialog; 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; /** @@ -38,7 +43,7 @@ class BluetoothHandler { private Activity mActivity; private OutputStream mOutStream; - private InputStream mInputStream; + private BufferedReader mReader; /** * Constructor @@ -83,6 +88,7 @@ protected void createConnection() { /** * Open the input and output communication with Arduino. */ + @RequiresApi(api = Build.VERSION_CODES.KITKAT) private void connect() { OutputStream tmpOut = null; InputStream tmpIn = null; @@ -95,7 +101,7 @@ private void connect() { Log.e(Ardutooth.TAG, "Error occurred when creating output stream", e); } mOutStream = tmpOut; - mInputStream = tmpIn; + mReader = new BufferedReader(new InputStreamReader(tmpIn, StandardCharsets.US_ASCII)); } catch (IOException e) { Log.e(Ardutooth.TAG, "Error opening connection", e); closeConnection(); @@ -110,7 +116,7 @@ protected void closeConnection() { try { mSocket.close(); mOutStream.close(); - mInputStream.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(); @@ -229,9 +235,13 @@ protected OutputStream getOutputStream() { return mOutStream; } - protected InputStream getInputStream() { return mInputStream; } + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + protected BufferedReader getInputReader() { + return mReader; + } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); From 8b621c71e70f0616b7f1d081120d8518aba46b73 Mon Sep 17 00:00:00 2001 From: grabinow2 Date: Wed, 13 May 2020 18:23:41 -0400 Subject: [PATCH 5/6] Added ability to connect with already paired device. In checkBluetoothState(), added a step to connection process, wherein, if Bluetooth is already on, no device is connected, but there are bonded (paired) devices, a dialog will prompt the user to connect with one of the already paired devices from a list displayed. The user still has the option to pair with a new device if the right one isn't present. --- .../ardutooth/BluetoothHandler.java | 181 +++++++++++++++--- ardutooth/src/main/res/values/strings.xml | 4 +- 2 files changed, 154 insertions(+), 31 deletions(-) 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 c991cbe..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; @@ -11,8 +12,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.os.Build; -import android.support.annotation.RequiresApi; import android.support.v7.app.AlertDialog; import android.util.Log; import android.widget.Toast; @@ -24,6 +23,7 @@ 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. @@ -66,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 @@ -87,9 +87,11 @@ protected void createConnection() { /** * Open the input and output communication with Arduino. + * + * @return true if communication has been set up successfully, else false */ - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - private void connect() { + @SuppressLint("NewApi") + private boolean connect() { OutputStream tmpOut = null; InputStream tmpIn = null; try { @@ -106,6 +108,7 @@ private void connect() { Log.e(Ardutooth.TAG, "Error opening connection", e); closeConnection(); } + return mSocket.isConnected(); } /** @@ -130,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"); + } /** @@ -149,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()) { @@ -177,23 +186,18 @@ public void onClick(DialogInterface dialog, int which) { Log.d(Ardutooth.TAG, "Bluetooth is already on"); retrieveConnection(); if (!connected) { - 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 (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(); } @@ -211,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() { @@ -220,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() { @@ -229,30 +344,36 @@ protected BluetoothSocket getSocket() { /** * Retrieve the {@link OutputStream} of the communication + * * @return the {@link OutputStream} of the communication */ protected OutputStream getOutputStream() { return mOutStream; } - @RequiresApi(api = Build.VERSION_CODES.KITKAT) + /** + * 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() { - @RequiresApi(api = Build.VERSION_CODES.KITKAT) @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); } 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 From 6ca5eb74714b9773df30df70b704e39bd9446d4a Mon Sep 17 00:00:00 2001 From: grabinow2 Date: Wed, 13 May 2020 18:24:39 -0400 Subject: [PATCH 6/6] Corrected some comments --- .../github/giuseppebrb/ardutooth/Ardutooth.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) 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 796605a..d2d856a 100644 --- a/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/Ardutooth.java +++ b/ardutooth/src/main/java/io/github/giuseppebrb/ardutooth/Ardutooth.java @@ -15,7 +15,7 @@ /** * 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 @@ -70,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; } /** @@ -213,11 +213,11 @@ public void sendBoolean(boolean value) { } /** - * Reads a single byte from the Arduino, casts it into a {@link char}, and returns it. + * 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 {@link char} value read + * @return char value read */ - @RequiresApi(api = Build.VERSION_CODES.KITKAT) public char receiveChar(){ char c = 0; if (mBtHandler.getSocket() != null) @@ -230,12 +230,11 @@ public char receiveChar(){ } /** - * Reads from the Arduino until a return character ("\n") is read, and returns the full line. + * Reads a line, as defined by {@link BufferedReader}'s readLine method, from the Arduino. * * @return {@link String} line read */ - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - public String receiveLn(){ + public String receiveLine(){ String result = ""; if (mBtHandler.getSocket() != null) try {