mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2026-01-05 07:44:20 +01:00
Implementing request queue in BleManager
Both BleManagers (app and wear) modified, All profiles adjusted to match new API, Supporting Bluetooth OFF/ON event in BleProfileService
This commit is contained in:
@@ -27,14 +27,14 @@ import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.BloodPressureMeasurementParser;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.IntermediateCuffPressureParser;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
|
||||
public class BPMManager extends BleManager<BPMManagerCallbacks> {
|
||||
/** Blood Pressure service UUID */
|
||||
@@ -73,7 +73,7 @@ public class BPMManager extends BleManager<BPMManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
if (mICPCharacteristic != null)
|
||||
requests.add(Request.newEnableNotificationsRequest(mICPCharacteristic));
|
||||
|
||||
@@ -28,8 +28,8 @@ import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
@@ -128,7 +128,7 @@ public class CGMSManager extends BleManager<CGMSManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
requests.add(Request.newEnableNotificationsRequest(mCGMMeasurementCharacteristic));
|
||||
if (mCGMOpsControlPointCharacteristic != null) {
|
||||
|
||||
@@ -161,12 +161,6 @@ public class CGMService extends BleProfileService implements CGMSManagerCallback
|
||||
createNotification(R.string.csc_notification_connected_message, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceStarted() {
|
||||
// logger is now available. Assign it to the manager
|
||||
mManager.setLogger(getLogSession());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the notification
|
||||
*
|
||||
|
||||
@@ -27,13 +27,13 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.CSCMeasurementParser;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
|
||||
public class CSCManager extends BleManager<CSCManagerCallbacks> {
|
||||
/** Cycling Speed and Cadence service UUID */
|
||||
@@ -61,7 +61,7 @@ public class CSCManager extends BleManager<CSCManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
requests.add(Request.newEnableNotificationsRequest(mCSCMeasurementCharacteristic));
|
||||
return requests;
|
||||
|
||||
@@ -120,12 +120,6 @@ public class CSCService extends BleProfileService implements CSCManagerCallbacks
|
||||
createNotification(R.string.csc_notification_connected_message, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceStarted() {
|
||||
// logger is now available. Assign it to the manager
|
||||
mManager.setLogger(getLogSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWheelMeasurementReceived(final BluetoothDevice device, final int wheelRevolutions, final int lastWheelEventTime) {
|
||||
Logger.a(getLogSession(), "Wheel rev: " + wheelRevolutions + "\nLast wheel event time: " + lastWheelEventTime + " ms");
|
||||
|
||||
@@ -29,15 +29,15 @@ import android.os.Handler;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.GlucoseMeasurementContextParser;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.GlucoseMeasurementParser;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.RecordAccessControlPointParser;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -127,7 +127,7 @@ public class GlucoseManager extends BleManager<GlucoseManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
requests.add(Request.newEnableNotificationsRequest(mGlucoseMeasurementCharacteristic));
|
||||
if (mGlucoseMeasurementContextCharacteristic != null)
|
||||
|
||||
@@ -26,15 +26,15 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
import no.nordicsemi.android.nrftoolbox.R;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.BodySensorLocationParser;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.HeartRateMeasurementParser;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
|
||||
/**
|
||||
* HRSManager class performs BluetoothGatt operations for connection, service discovery, enabling notification and reading characteristics. All operations required to connect to device with BLE HR
|
||||
@@ -74,7 +74,7 @@ public class HRSManager extends BleManager<HRSManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
if (mHRLocationCharacteristic != null)
|
||||
requests.add(Request.newReadRequest(mHRLocationCharacteristic));
|
||||
|
||||
@@ -26,8 +26,8 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
@@ -71,7 +71,7 @@ public class HTSManager extends BleManager<HTSManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
requests.add(Request.newEnableIndicationsRequest(mHTCharacteristic));
|
||||
return requests;
|
||||
|
||||
@@ -100,12 +100,6 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks
|
||||
createNotification(R.string.hts_notification_connected_message, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceStarted() {
|
||||
// logger is now available. Assign it to the manager
|
||||
mManager.setLogger(getLogSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHTValueReceived(final BluetoothDevice device, final double value) {
|
||||
final Intent broadcast = new Intent(BROADCAST_HTS_MEASUREMENT);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -86,6 +86,18 @@ public interface BleManagerCallbacks {
|
||||
*/
|
||||
void onDeviceReady(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* This method should return true if Battery Level notifications should be enabled on the target device.
|
||||
* If there is no Battery Service, or the Battery Level characteristic does not have NOTIFY property,
|
||||
* this method will not be called for this device.
|
||||
* <p>This method may return true only if an activity is bound to the service (to display the information
|
||||
* to the user), always (e.g. if critical battery level is reported using notifications) or never, if
|
||||
* such information is not important or the manager wants to control Battery Level notifications on its own.</p>
|
||||
* @param device target device
|
||||
* @return true to enabled battery level notifications after connecting to the device, false otherwise
|
||||
*/
|
||||
boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when battery value has been received from the device.
|
||||
*
|
||||
|
||||
@@ -52,9 +52,9 @@ import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
|
||||
public abstract class BleProfileActivity extends AppCompatActivity implements BleManagerCallbacks, ScannerFragment.OnDeviceSelectedListener {
|
||||
private static final String TAG = "BaseProfileActivity";
|
||||
|
||||
private static final String CONNECTION_STATUS = "connection_status";
|
||||
private static final String DEVICE_NAME = "device_name";
|
||||
private static final int REQUEST_ENABLE_BT = 2;
|
||||
private static final String SIS_CONNECTION_STATUS = "connection_status";
|
||||
private static final String SIS_DEVICE_NAME = "device_name";
|
||||
protected static final int REQUEST_ENABLE_BT = 2;
|
||||
|
||||
private BleManager<? extends BleManagerCallbacks> mBleManager;
|
||||
|
||||
@@ -82,12 +82,18 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
* broadcast listeners, local broadcast listeners (see support.v4 library), or messages. See the Proximity profile for Service approach.
|
||||
*/
|
||||
mBleManager = initializeManager();
|
||||
|
||||
// In onInitialize method a final class may register local broadcast receivers that will listen for events from the service
|
||||
onInitialize(savedInstanceState);
|
||||
// The onCreateView class should... create the view
|
||||
onCreateView(savedInstanceState);
|
||||
|
||||
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_actionbar);
|
||||
setSupportActionBar(toolbar);
|
||||
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_actionbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
// Common nRF Toolbox view references are obtained here
|
||||
setUpView();
|
||||
// View is ready to be used
|
||||
onViewCreated(savedInstanceState);
|
||||
}
|
||||
|
||||
@@ -101,18 +107,24 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
/**
|
||||
* Called from {@link #onCreate(Bundle)}. This method should build the activity UI, f.e. using {@link #setContentView(int)}. Use to obtain references to
|
||||
* views. Connect/Disconnect button, the device name view and battery level view are manager automatically.
|
||||
*
|
||||
* @param savedInstanceState
|
||||
* contains the data it most recently supplied in {@link #onSaveInstanceState(Bundle)}. Note: <b>Otherwise it is null</b>.
|
||||
*
|
||||
* @param savedInstanceState contains the data it most recently supplied in {@link #onSaveInstanceState(Bundle)}. Note: <b>Otherwise it is null</b>.
|
||||
*/
|
||||
protected abstract void onCreateView(final Bundle savedInstanceState);
|
||||
|
||||
/**
|
||||
* Called after the view has been created.
|
||||
*
|
||||
* @param savedInstanceState
|
||||
*
|
||||
* @param savedInstanceState contains the data it most recently supplied in {@link #onSaveInstanceState(Bundle)}. Note: <b>Otherwise it is null</b>.
|
||||
*/
|
||||
protected final void onViewCreated(final Bundle savedInstanceState) {
|
||||
protected void onViewCreated(final Bundle savedInstanceState) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the view and the toolbar has been created.
|
||||
*/
|
||||
protected final void setUpView() {
|
||||
// set GUI
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
mConnectButton = (Button) findViewById(R.id.action_connect);
|
||||
@@ -129,15 +141,15 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
@Override
|
||||
protected void onSaveInstanceState(final Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(CONNECTION_STATUS, mDeviceConnected);
|
||||
outState.putString(DEVICE_NAME, mDeviceName);
|
||||
outState.putBoolean(SIS_CONNECTION_STATUS, mDeviceConnected);
|
||||
outState.putString(SIS_DEVICE_NAME, mDeviceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
mDeviceConnected = savedInstanceState.getBoolean(CONNECTION_STATUS);
|
||||
mDeviceName = savedInstanceState.getString(DEVICE_NAME);
|
||||
mDeviceConnected = savedInstanceState.getBoolean(SIS_CONNECTION_STATUS);
|
||||
mDeviceName = savedInstanceState.getString(SIS_DEVICE_NAME);
|
||||
|
||||
if (mDeviceConnected) {
|
||||
mConnectButton.setText(R.string.action_disconnect);
|
||||
@@ -154,9 +166,8 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
|
||||
/**
|
||||
* Use this method to handle menu actions other than home and about.
|
||||
*
|
||||
* @param itemId
|
||||
* the menu item id
|
||||
*
|
||||
* @param itemId the menu item id
|
||||
* @return <code>true</code> if action has been handled
|
||||
*/
|
||||
protected boolean onOptionsItemSelected(final int itemId) {
|
||||
@@ -168,15 +179,15 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
final int id = item.getItemId();
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
break;
|
||||
case R.id.action_about:
|
||||
final AppHelpFragment fragment = AppHelpFragment.getInstance(getAboutTextId());
|
||||
fragment.show(getSupportFragmentManager(), "help_fragment");
|
||||
break;
|
||||
default:
|
||||
return onOptionsItemSelected(id);
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
break;
|
||||
case R.id.action_about:
|
||||
final AppHelpFragment fragment = AppHelpFragment.getInstance(getAboutTextId());
|
||||
fragment.show(getSupportFragmentManager(), "help_fragment");
|
||||
break;
|
||||
default:
|
||||
return onOptionsItemSelected(id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -229,7 +240,6 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
mBleManager.setLogger(mLogSession);
|
||||
mDeviceNameView.setText(name != null ? name : getString(R.string.not_available));
|
||||
mConnectButton.setText(R.string.action_connecting);
|
||||
mConnectButton.setEnabled(false);
|
||||
mBleManager.connect(device);
|
||||
}
|
||||
|
||||
@@ -250,7 +260,6 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectButton.setText(R.string.action_disconnect);
|
||||
mConnectButton.setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -270,7 +279,6 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
mConnectButton.setText(R.string.action_connect);
|
||||
mDeviceNameView.setText(getDefaultDeviceName());
|
||||
mBatteryLevelView.setText(R.string.not_available);
|
||||
mConnectButton.setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -281,21 +289,20 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectButton.setText(R.string.action_connect);
|
||||
mDeviceNameView.setText(getDefaultDeviceName());
|
||||
mBatteryLevelView.setText(R.string.not_available);
|
||||
if (mBatteryLevelView != null)
|
||||
mBatteryLevelView.setText(R.string.not_available);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatteryValueReceived(final BluetoothDevice device, final int value) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mBatteryLevelView.setText(getString(R.string.battery, value));
|
||||
}
|
||||
});
|
||||
public void onServicesDiscovered(final BluetoothDevice device, boolean optionalServicesFound) {
|
||||
// this may notify user or show some views
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceReady(final BluetoothDevice device) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -308,6 +315,23 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
showToast(R.string.bonded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// Yes, we want battery level updates
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatteryValueReceived(final BluetoothDevice device, final int value) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mBatteryLevelView != null)
|
||||
mBatteryLevelView.setText(getString(R.string.battery, value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final BluetoothDevice device, final String message, final int errorCode) {
|
||||
DebugLogger.e(TAG, "Error occurred: " + message + ", error code: " + errorCode);
|
||||
@@ -321,9 +345,8 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
|
||||
/**
|
||||
* Shows a message as a Toast notification. This method is thread safe, you can call it from any thread
|
||||
*
|
||||
* @param message
|
||||
* a message to be shown
|
||||
*
|
||||
* @param message a message to be shown
|
||||
*/
|
||||
protected void showToast(final String message) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@@ -336,9 +359,8 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
|
||||
/**
|
||||
* Shows a message as a Toast notification. This method is thread safe, you can call it from any thread
|
||||
*
|
||||
* @param messageResId
|
||||
* an resource id of the message to be shown
|
||||
*
|
||||
* @param messageResId an resource id of the message to be shown
|
||||
*/
|
||||
protected void showToast(final int messageResId) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@@ -365,7 +387,7 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
|
||||
/**
|
||||
* Initializes the Bluetooth Low Energy manager. A manager is used to communicate with profile's services.
|
||||
*
|
||||
*
|
||||
* @return the manager that was created
|
||||
*/
|
||||
protected abstract BleManager<? extends BleManagerCallbacks> initializeManager();
|
||||
@@ -378,14 +400,14 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
/**
|
||||
* Returns the default device name resource id. The real device name is obtained when connecting to the device. This one is used when device has
|
||||
* disconnected.
|
||||
*
|
||||
*
|
||||
* @return the default device name resource id
|
||||
*/
|
||||
protected abstract int getDefaultDeviceName();
|
||||
|
||||
/**
|
||||
* Returns the string resource id that will be shown in About box
|
||||
*
|
||||
*
|
||||
* @return the about resource id
|
||||
*/
|
||||
protected abstract int getAboutTextId();
|
||||
@@ -393,17 +415,16 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
/**
|
||||
* The UUID filter is used to filter out available devices that does not have such UUID in their advertisement packet. See also:
|
||||
* {@link #isChangingConfigurations()}.
|
||||
*
|
||||
*
|
||||
* @return the required UUID or <code>null</code>
|
||||
*/
|
||||
protected abstract UUID getFilterUUID();
|
||||
|
||||
/**
|
||||
* Shows the scanner fragment.
|
||||
*
|
||||
* @param filter
|
||||
* the UUID filter used to filter out available devices. The fragment will always show all bonded devices as there is no information about their
|
||||
* services
|
||||
*
|
||||
* @param filter the UUID filter used to filter out available devices. The fragment will always show all bonded devices as there is no information about their
|
||||
* services
|
||||
* @see #getFilterUUID()
|
||||
*/
|
||||
private void showDeviceScanningDialog(final UUID filter) {
|
||||
@@ -416,6 +437,15 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the log session. Log session is created when the device was selected using the {@link ScannerFragment} and released when user press DISCONNECT.
|
||||
*
|
||||
* @return the logger session or <code>null</code>
|
||||
*/
|
||||
protected ILogSession getLogSession() {
|
||||
return mLogSession;
|
||||
}
|
||||
|
||||
private void ensureBLESupported() {
|
||||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||
Toast.makeText(this, R.string.no_ble, Toast.LENGTH_LONG).show();
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -81,12 +82,18 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
* broadcast listeners, local broadcast listeners (see support.v4 library), or messages. See the Proximity profile for Service approach.
|
||||
*/
|
||||
mBleManager = initializeManager();
|
||||
|
||||
// In onInitialize method a final class may register local broadcast receivers that will listen for events from the service
|
||||
onInitialize(savedInstanceState);
|
||||
// The onCreateView class should... create the view
|
||||
onCreateView(savedInstanceState);
|
||||
|
||||
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_actionbar);
|
||||
setSupportActionBar(toolbar);
|
||||
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_actionbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
// Common nRF Toolbox view references are obtained here
|
||||
setUpView();
|
||||
// View is ready to be used
|
||||
onViewCreated(savedInstanceState);
|
||||
}
|
||||
|
||||
@@ -110,7 +117,14 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
*
|
||||
* @param savedInstanceState contains the data it most recently supplied in {@link #onSaveInstanceState(Bundle)}. Note: <b>Otherwise it is null</b>.
|
||||
*/
|
||||
protected final void onViewCreated(final Bundle savedInstanceState) {
|
||||
protected void onViewCreated(final Bundle savedInstanceState) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the view and the toolbar has been created.
|
||||
*/
|
||||
protected final void setUpView() {
|
||||
// set GUI
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
mConnectButton = (Button) findViewById(R.id.action_connect);
|
||||
@@ -132,7 +146,7 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
|
||||
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
mDeviceConnected = savedInstanceState.getBoolean(SIS_CONNECTION_STATUS);
|
||||
mDeviceName = savedInstanceState.getString(SIS_DEVICE_NAME);
|
||||
@@ -226,7 +240,6 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
mBleManager.setLogger(mLogSession);
|
||||
mDeviceNameView.setText(name != null ? name : getString(R.string.not_available));
|
||||
mConnectButton.setText(R.string.action_connecting);
|
||||
mConnectButton.setEnabled(false);
|
||||
mBleManager.connect(device);
|
||||
}
|
||||
|
||||
@@ -247,7 +260,6 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectButton.setText(R.string.action_disconnect);
|
||||
mConnectButton.setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -277,9 +289,8 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectButton.setText(R.string.action_connect);
|
||||
mDeviceNameView.setText(getDefaultDeviceName());
|
||||
mBatteryLevelView.setText(R.string.not_available);
|
||||
if (mBatteryLevelView != null)
|
||||
mBatteryLevelView.setText(R.string.not_available);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -289,23 +300,11 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
// this may notify user or show some views
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the initialization process in completed.
|
||||
*/
|
||||
@Override
|
||||
public void onDeviceReady(final BluetoothDevice device) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatteryValueReceived(final BluetoothDevice device, final int value) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mBatteryLevelView.setText(getString(R.string.battery, value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBondingRequired(final BluetoothDevice device) {
|
||||
showToast(R.string.bonding);
|
||||
@@ -316,6 +315,23 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
showToast(R.string.bonded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// Yes, we want battery level updates
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatteryValueReceived(final BluetoothDevice device, final int value) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mBatteryLevelView != null)
|
||||
mBatteryLevelView.setText(getString(R.string.battery, value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final BluetoothDevice device, final String message, final int errorCode) {
|
||||
DebugLogger.e(TAG, "Error occurred: " + message + ", error code: " + errorCode);
|
||||
@@ -421,6 +437,15 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the log session. Log session is created when the device was selected using the {@link ScannerFragment} and released when user press DISCONNECT.
|
||||
*
|
||||
* @return the logger session or <code>null</code>
|
||||
*/
|
||||
protected ILogSession getLogSession() {
|
||||
return mLogSession;
|
||||
}
|
||||
|
||||
private void ensureBLESupported() {
|
||||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||
Toast.makeText(this, R.string.no_ble, Toast.LENGTH_LONG).show();
|
||||
|
||||
@@ -24,16 +24,22 @@ package no.nordicsemi.android.nrftoolbox.profile;
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import no.nordicsemi.android.log.ILogSession;
|
||||
import no.nordicsemi.android.log.LogContract;
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
|
||||
public abstract class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
@@ -71,18 +77,54 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
private Handler mHandler;
|
||||
|
||||
protected boolean mBinded;
|
||||
private boolean mActivityFinished;
|
||||
private boolean mConnected;
|
||||
private boolean mActivityIsChangingConfiguration;
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
private String mDeviceName;
|
||||
private ILogSession mLogSession;
|
||||
|
||||
public class LocalBinder extends Binder {
|
||||
private final BroadcastReceiver mBluetoothStateBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
|
||||
final ILogger logger = getBinder();
|
||||
|
||||
final String stateString = "[Broadcast] Action received: " + BluetoothAdapter.ACTION_STATE_CHANGED + ", state changed to " + state2String(state);
|
||||
logger.log(LogContract.Log.Level.DEBUG, stateString);
|
||||
|
||||
switch (state) {
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
onBluetoothEnabled();
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
onBluetoothDisabled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private String state2String(final int state) {
|
||||
switch (state) {
|
||||
case BluetoothAdapter.STATE_TURNING_ON:
|
||||
return "TURNING ON";
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
return "ON";
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
return "TURNING OFF";
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
return "OFF";
|
||||
default:
|
||||
return "UNKNOWN (" + state + ")";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public class LocalBinder extends Binder implements ILogger {
|
||||
/**
|
||||
* Disconnects from the sensor.
|
||||
*/
|
||||
public final void disconnect() {
|
||||
if (!mConnected) {
|
||||
final int state = mBleManager.getConnectionState();
|
||||
if (state == BluetoothGatt.STATE_DISCONNECTED || state == BluetoothGatt.STATE_DISCONNECTING) {
|
||||
mBleManager.close();
|
||||
onDeviceDisconnected(mBluetoothDevice);
|
||||
return;
|
||||
@@ -92,16 +134,17 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the binded activity if finishing or not. If <code>true</code>, we will turn off battery level notifications in onUnbind(..) method below.
|
||||
* @param finishing true if the binded activity is finishing
|
||||
* Sets whether the bound activity if changing configuration or not.
|
||||
* If <code>false</code>, we will turn off battery level notifications in onUnbind(..) method below.
|
||||
* @param changing true if the bound activity is finishing
|
||||
*/
|
||||
public void setActivityIsFinishing(final boolean finishing) {
|
||||
mActivityFinished = finishing;
|
||||
public void setActivityIsChangingConfiguration(final boolean changing) {
|
||||
mActivityIsChangingConfiguration = changing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device address
|
||||
*
|
||||
*
|
||||
* @return device address
|
||||
*/
|
||||
public String getDeviceAddress() {
|
||||
@@ -110,7 +153,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
|
||||
/**
|
||||
* Returns the device name
|
||||
*
|
||||
*
|
||||
* @return the device name
|
||||
*/
|
||||
public String getDeviceName() {
|
||||
@@ -128,26 +171,46 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the device is connected to the sensor.
|
||||
*
|
||||
*
|
||||
* @return <code>true</code> if device is connected to the sensor, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return mConnected;
|
||||
return mBleManager.isConnected();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection state of given device.
|
||||
* @return the connection state, as in {@link BleManager#getConnectionState()}.
|
||||
*/
|
||||
public int getConnectionState() {
|
||||
return mBleManager.getConnectionState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the log session that can be used to append log entries. The log session is created when the service is being created. The method returns <code>null</code> if the nRF Logger app was
|
||||
* not installed.
|
||||
* Returns the log session that can be used to append log entries.
|
||||
* The log session is created when the service is being created.
|
||||
* The method returns <code>null</code> if the nRF Logger app was not installed.
|
||||
*
|
||||
* @return the log session
|
||||
*/
|
||||
public ILogSession getLogSession() {
|
||||
return mLogSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(final int level, final String message) {
|
||||
Logger.log(mLogSession, level, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(final int level, final @StringRes int messageRes, final Object... params) {
|
||||
Logger.log(mLogSession, level, messageRes, params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the binder implementation. This must return class implementing the additional manager interface that may be used in the binded activity.
|
||||
* Returns the binder implementation. This must return class implementing the additional manager interface that may be used in the bound activity.
|
||||
*
|
||||
* @return the service binder
|
||||
*/
|
||||
@@ -166,11 +229,10 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
public final void onRebind(final Intent intent) {
|
||||
mBinded = true;
|
||||
|
||||
if (mActivityFinished)
|
||||
if (!mActivityIsChangingConfiguration)
|
||||
onRebind();
|
||||
|
||||
if (mActivityFinished && mConnected) {
|
||||
mActivityFinished = false;
|
||||
if (!mActivityIsChangingConfiguration && mBleManager.isConnected()) {
|
||||
// This method will read the Battery Level value, if possible and then try to enable battery notifications (if it has NOTIFY property).
|
||||
// If the Battery Level characteristic has only the NOTIFY property, it will only try to enable notifications.
|
||||
mBleManager.readBatteryLevel();
|
||||
@@ -178,33 +240,35 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity has rebinded to the service after being recreated. This method is not called when the activity was killed and recreated just to change the phone orientation.
|
||||
* Called when the activity has rebound to the service after being recreated.
|
||||
* This method is not called when the activity was killed to be recreated when the phone orientation changed
|
||||
* if prior to being killed called {@link BleProfileService.LocalBinder#setActivityIsChangingConfiguration(boolean)} with parameter true.
|
||||
*/
|
||||
protected void onRebind() {
|
||||
// empty
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onUnbind(final Intent intent) {
|
||||
mBinded = false;
|
||||
|
||||
if (mActivityFinished)
|
||||
if (!mActivityIsChangingConfiguration)
|
||||
onUnbind();
|
||||
|
||||
// When we are connected, but the application is not open, we are not really interested in battery level notifications. But we will still be receiving other values, if enabled.
|
||||
if (mActivityFinished && mConnected)
|
||||
if (!mActivityIsChangingConfiguration && mBleManager.isConnected())
|
||||
mBleManager.setBatteryNotifications(false);
|
||||
|
||||
// we must allow to rebind to the same service
|
||||
// We want the onRebind method be called if anything else binds to it again
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity has unbound from the service before being finished.
|
||||
* This method is not called when the activity is killed to be recreated just to change the phone orientation.
|
||||
* This method is not called when the activity is killed to be recreated when the phone orientation changed.
|
||||
*/
|
||||
protected void onUnbind() {
|
||||
// empty
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -214,11 +278,34 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
|
||||
mHandler = new Handler();
|
||||
|
||||
// initialize the manager
|
||||
// Initialize the manager
|
||||
mBleManager = initializeManager();
|
||||
mBleManager.setGattCallbacks(this);
|
||||
|
||||
// Register broadcast receivers
|
||||
registerReceiver(mBluetoothStateBroadcastReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
||||
|
||||
// Service has now been created
|
||||
onServiceCreated();
|
||||
|
||||
// Call onBluetoothEnabled if Bluetooth enabled
|
||||
final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter.isEnabled()) {
|
||||
onBluetoothEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the service has been created, before the {@link #onBluetoothEnabled()} is called.
|
||||
*/
|
||||
protected void onServiceCreated() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Ble Manager responsible for connecting to a single device.
|
||||
* @return a new BleManager object
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected abstract BleManager initializeManager();
|
||||
|
||||
@@ -237,22 +324,35 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
final BluetoothAdapter adapter = bluetoothManager.getAdapter();
|
||||
final String deviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
|
||||
mBluetoothDevice = adapter.getRemoteDevice(deviceAddress);
|
||||
onServiceStarted();
|
||||
|
||||
mBleManager.setLogger(mLogSession);
|
||||
onServiceStarted();
|
||||
mBleManager.connect(mBluetoothDevice);
|
||||
return START_REDELIVER_INTENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the service has been started. The device name and address are set. It nRF Logger is installed than logger was also initialized.
|
||||
* Called when the service has been started. The device name and address are set.
|
||||
* The BLE Manager will try to connect to the device after this method finishes.
|
||||
*/
|
||||
protected void onServiceStarted() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskRemoved(final Intent rootIntent) {
|
||||
super.onTaskRemoved(rootIntent);
|
||||
// This method is called when user removed the app from Recents.
|
||||
// By default, the service will be killed and recreated immediately after that.
|
||||
// However, all managed devices will be lost and devices will be disconnected.
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Unregister broadcast receivers
|
||||
unregisterReceiver(mBluetoothStateBroadcastReceiver);
|
||||
|
||||
// shutdown the manager
|
||||
mBleManager.close();
|
||||
@@ -260,10 +360,31 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
mBleManager = null;
|
||||
mBluetoothDevice = null;
|
||||
mDeviceName = null;
|
||||
mConnected = false;
|
||||
mLogSession = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when Bluetooth Adapter has been disabled.
|
||||
*/
|
||||
protected void onBluetoothDisabled() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when Bluetooth Adapter has been enabled and
|
||||
* after the service was created if Bluetooth Adapter was enabled at that moment.
|
||||
* This method could initialize all Bluetooth related features, for example open the GATT server.
|
||||
*/
|
||||
protected void onBluetoothEnabled() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// By default the Battery Level notifications will be enabled only the activity is bound.
|
||||
return mBinded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnecting(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
@@ -274,8 +395,6 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
|
||||
@Override
|
||||
public void onDeviceConnected(final BluetoothDevice device) {
|
||||
mConnected = true;
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTED);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
@@ -303,8 +422,6 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnected(final BluetoothDevice device) {
|
||||
mConnected = false;
|
||||
|
||||
// Do not use the device argument here unless you change calling onDeviceDisconnected from the binder above
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
@@ -323,8 +440,6 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
|
||||
@Override
|
||||
public void onLinklossOccur(final BluetoothDevice device) {
|
||||
mConnected = false;
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_LINK_LOSS);
|
||||
@@ -394,6 +509,8 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
broadcast.putExtra(EXTRA_ERROR_CODE, errorCode);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
|
||||
// After receiving an error the device will be automatically disconnected.
|
||||
// Replace it with other implementation if necessary.
|
||||
mBleManager.disconnect();
|
||||
stopSelf();
|
||||
}
|
||||
@@ -471,6 +588,6 @@ public abstract class BleProfileService extends Service implements BleManagerCal
|
||||
* @return <code>true</code> if device is connected to the sensor, <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean isConnected() {
|
||||
return mConnected;
|
||||
return mBleManager.isConnected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,21 +179,30 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
final E bleService = mService = (E) service;
|
||||
mBluetoothDevice = bleService.getBluetoothDevice();
|
||||
mLogSession = mService.getLogSession();
|
||||
Logger.d(mLogSession, "Activity binded to the service");
|
||||
Logger.d(mLogSession, "Activity bound to the service");
|
||||
onServiceBinded(bleService);
|
||||
|
||||
// update UI
|
||||
// Update UI
|
||||
mDeviceName = bleService.getDeviceName();
|
||||
mDeviceNameView.setText(mDeviceName);
|
||||
mConnectButton.setText(R.string.action_disconnect);
|
||||
|
||||
// and notify user if device is connected
|
||||
if (bleService.isConnected())
|
||||
// And notify user if device is connected
|
||||
if (bleService.isConnected()) {
|
||||
onDeviceConnected(mBluetoothDevice);
|
||||
} else {
|
||||
// If the device is not connected it means that either it is still connecting,
|
||||
// or the link was lost and service is trying to connect to it (autoConnect=true).
|
||||
onDeviceConnecting(mBluetoothDevice);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(final ComponentName name) {
|
||||
// Note: this method is called only when the service is killed by the system,
|
||||
// not when it stops itself or is stopped by the activity.
|
||||
// It will be called only when there is critically low memory, in practice never
|
||||
// when the activity is in foreground.
|
||||
Logger.d(mLogSession, "Activity disconnected from the service");
|
||||
mDeviceNameView.setText(getDefaultDeviceName());
|
||||
mConnectButton.setText(R.string.action_connect);
|
||||
@@ -221,33 +230,32 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
mLogSession = Logger.openSession(getApplicationContext(), logUri);
|
||||
}
|
||||
|
||||
/*
|
||||
* In this example we use the ProximityManager in the service. This class communicates with the service using local broadcasts. Final activity may bind
|
||||
* to the Server to use its interface.
|
||||
*/
|
||||
// In onInitialize method a final class may register local broadcast receivers that will listen for events from the service
|
||||
onInitialize(savedInstanceState);
|
||||
// The onCreateView class should... create the view
|
||||
onCreateView(savedInstanceState);
|
||||
|
||||
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_actionbar);
|
||||
setSupportActionBar(toolbar);
|
||||
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_actionbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
// Common nRF Toolbox view references are obtained here
|
||||
setUpView();
|
||||
// View is ready to be used
|
||||
onViewCreated(savedInstanceState);
|
||||
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mCommonBroadcastReceiver, makeIntentFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
/*
|
||||
* If the service has not been started before, the following lines will not start it. However, if it's running, the Activity will be binded to it and
|
||||
* If the service has not been started before, the following lines will not start it. However, if it's running, the Activity will bind to it and
|
||||
* notified via mServiceConnection.
|
||||
*/
|
||||
final Intent service = new Intent(this, getServiceClass());
|
||||
if (bindService(service, mServiceConnection, 0)) // we pass 0 as a flag so the service will not be created if not exists
|
||||
Logger.d(mLogSession, "Binding to the service..."); // (* - see the comment below)
|
||||
bindService(service, mServiceConnection, 0); // we pass 0 as a flag so the service will not be created if not exists
|
||||
|
||||
/*
|
||||
* * - When user exited the UARTActivity while being connected, the log session is kept in the service. We may not get it before binding to it so in this
|
||||
@@ -256,20 +264,19 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
try {
|
||||
// We don't want to perform some operations (e.g. disable Battery Level notifications) in the service if we are just rotating the screen.
|
||||
// However, when the activity is finishing, we may want to disable some device features to reduce the battery consumption.
|
||||
// However, when the activity will disappear, we may want to disable some device features to reduce the battery consumption.
|
||||
if (mService != null)
|
||||
mService.setActivityIsFinishing(isFinishing());
|
||||
mService.setActivityIsChangingConfiguration(isChangingConfigurations());
|
||||
|
||||
Logger.d(mLogSession, "Unbinding from the service...");
|
||||
unbindService(mServiceConnection);
|
||||
mService = null;
|
||||
|
||||
Logger.d(mLogSession, "Activity unbinded from the service");
|
||||
Logger.d(mLogSession, "Activity unbound from the service");
|
||||
onServiceUnbinded();
|
||||
mDeviceName = null;
|
||||
mBluetoothDevice = null;
|
||||
@@ -460,7 +467,6 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
mDeviceName = name;
|
||||
mDeviceNameView.setText(name != null ? name : getString(R.string.not_available));
|
||||
mConnectButton.setText(R.string.action_connecting);
|
||||
mConnectButton.setEnabled(false);
|
||||
|
||||
// The device may not be in the range but the service will try to connect to it if it reach it
|
||||
Logger.d(mLogSession, "Creating service...");
|
||||
@@ -488,7 +494,6 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
public void onDeviceConnected(final BluetoothDevice device) {
|
||||
mDeviceNameView.setText(mDeviceName);
|
||||
mConnectButton.setText(R.string.action_disconnect);
|
||||
mConnectButton.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -545,8 +550,10 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceNotSupported(final BluetoothDevice device) {
|
||||
showToast(R.string.not_supported);
|
||||
public final boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// This method will never be called.
|
||||
// Please see BleProfileService#shouldEnableBatteryLevelNotifications(BluetoothDevice) instead.
|
||||
throw new UnsupportedOperationException("This method should not be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -561,6 +568,11 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
showToast(message + " (" + errorCode + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceNotSupported(final BluetoothDevice device) {
|
||||
showToast(R.string.not_supported);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a message as a Toast notification. This method is thread safe, you can call it from any thread
|
||||
*
|
||||
@@ -593,7 +605,7 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
* Returns <code>true</code> if the device is connected. Services may not have been discovered yet.
|
||||
*/
|
||||
protected boolean isDeviceConnected() {
|
||||
return mService != null;
|
||||
return mService != null && mService.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -658,7 +670,7 @@ public abstract class BleProfileServiceReadyActivity<E extends BleProfileService
|
||||
*
|
||||
* @return the logger session or <code>null</code>
|
||||
*/
|
||||
public ILogSession getLogSession() {
|
||||
protected ILogSession getLogSession() {
|
||||
return mLogSession;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.profile;
|
||||
|
||||
import android.support.annotation.StringRes;
|
||||
|
||||
public interface ILogger {
|
||||
|
||||
/**
|
||||
* Logs the given message with given log level into the all managed devices' log session.
|
||||
* @param level the log level
|
||||
* @param message the message to be logged
|
||||
*/
|
||||
void log(final int level, final String message);
|
||||
|
||||
/**
|
||||
* Logs the given message with given log level into the all managed devices' log session.
|
||||
* @param level the log level
|
||||
* @param messageRes string resource id
|
||||
* @param params additional (optional) parameters used to fill the message
|
||||
*/
|
||||
void log(final int level, @StringRes final int messageRes, final Object... params);
|
||||
}
|
||||
@@ -35,8 +35,8 @@ import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.error.GattError;
|
||||
@@ -277,7 +277,7 @@ public class ProximityManager extends BleManager<ProximityManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
requests.add(Request.newWriteRequest(mLinklossCharacteristic, HIGH_ALERT));
|
||||
return requests;
|
||||
|
||||
@@ -163,12 +163,6 @@ public class ProximityService extends BleProfileService implements ProximityMana
|
||||
createNotification(R.string.proximity_notification_linkloss_alert, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceStarted() {
|
||||
// logger is now available. Assign it to the manager
|
||||
mProximityManager.setLogger(getLogSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnecting(final BluetoothDevice device) {
|
||||
stopAlarm();
|
||||
|
||||
@@ -27,13 +27,13 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.RSCMeasurementParser;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
|
||||
public class RSCManager extends BleManager<RSCManagerCallbacks> {
|
||||
private static final byte INSTANTANEOUS_STRIDE_LENGTH_PRESENT = 0x01; // 1 bit
|
||||
@@ -62,7 +62,7 @@ public class RSCManager extends BleManager<RSCManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
requests.add(Request.newEnableNotificationsRequest(mRSCMeasurementCharacteristic));
|
||||
return requests;
|
||||
|
||||
@@ -121,12 +121,6 @@ public class RSCService extends BleProfileService implements RSCManagerCallbacks
|
||||
createNotification(R.string.rsc_notification_connected_message, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceStarted() {
|
||||
// logger is now available. Assign it to the manager
|
||||
mManager.setLogger(getLogSession());
|
||||
}
|
||||
|
||||
private final Runnable mUpdateStridesTask = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -26,13 +26,13 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.parser.TemplateParser;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
|
||||
/**
|
||||
* Modify to template manager to match your requirements.
|
||||
@@ -63,7 +63,7 @@ public class TemplateManager extends BleManager<TemplateManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
// TODO initialize your device, enable required notifications and indications, write what needs to be written to start working
|
||||
requests.add(Request.newEnableNotificationsRequest(mCharacteristic));
|
||||
|
||||
@@ -106,12 +106,6 @@ public class TemplateService extends BleProfileService implements TemplateManage
|
||||
createNotification(R.string.template_notification_connected_message, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceStarted() {
|
||||
// logger is now available. Assign it to the manager
|
||||
mManager.setLogger(getLogSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampleValueReceived(final BluetoothDevice device, final int value) {
|
||||
final Intent broadcast = new Intent(BROADCAST_TEMPLATE_MEASUREMENT);
|
||||
|
||||
@@ -29,8 +29,8 @@ import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
@@ -65,7 +65,7 @@ public class UARTManager extends BleManager<UARTManagerCallbacks> {
|
||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
||||
|
||||
@Override
|
||||
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<Request> initGatt(final BluetoothGatt gatt) {
|
||||
final LinkedList<Request> requests = new LinkedList<>();
|
||||
requests.add(Request.newEnableNotificationsRequest(mTXCharacteristic));
|
||||
return requests;
|
||||
@@ -116,11 +116,8 @@ public class UARTManager extends BleManager<UARTManagerCallbacks> {
|
||||
mOutgoingBuffer = null;
|
||||
} else { // Otherwise...
|
||||
final int length = Math.min(buffer.length - mBufferOffset, MAX_PACKET_SIZE);
|
||||
final byte[] data = new byte[length]; // We send at most 20 bytes
|
||||
System.arraycopy(buffer, mBufferOffset, data, 0, length);
|
||||
enqueue(Request.newWriteRequest(mRXCharacteristic, buffer, mBufferOffset, length));
|
||||
mBufferOffset += length;
|
||||
mRXCharacteristic.setValue(data);
|
||||
writeCharacteristic(mRXCharacteristic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,15 +155,12 @@ public class UARTManager extends BleManager<UARTManagerCallbacks> {
|
||||
|
||||
if (!writeRequest) { // no WRITE REQUEST property
|
||||
final int length = Math.min(buffer.length, MAX_PACKET_SIZE);
|
||||
final byte[] data = new byte[length]; // We send at most 20 bytes
|
||||
System.arraycopy(buffer, 0, data, 0, length);
|
||||
mBufferOffset += length;
|
||||
mRXCharacteristic.setValue(data);
|
||||
} else { // there is WRITE REQUEST property
|
||||
mRXCharacteristic.setValue(buffer);
|
||||
enqueue(Request.newWriteRequest(mRXCharacteristic, buffer, 0, length));
|
||||
} else { // there is WRITE REQUEST property, let's try Long Write
|
||||
mBufferOffset = buffer.length;
|
||||
enqueue(Request.newWriteRequest(mRXCharacteristic, buffer, 0, buffer.length));
|
||||
}
|
||||
writeCharacteristic(mRXCharacteristic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,11 +83,6 @@ public class UARTService extends BleProfileService implements UARTManagerCallbac
|
||||
public void send(final String text) {
|
||||
mManager.send(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILogSession getLogSession() {
|
||||
return super.getLogSession();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,9 +133,9 @@ public class UARTService extends BleProfileService implements UARTManagerCallbac
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceStarted() {
|
||||
// logger is now available. Assign it to the manager
|
||||
mManager.setLogger(getLogSession());
|
||||
public boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// No UI in UART profile for Battery Level information
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -51,4 +51,17 @@ public class ParserUtils {
|
||||
}
|
||||
return "(0x) " + new String(out);
|
||||
}
|
||||
|
||||
public static String parseDebug(final byte[] data) {
|
||||
if (data == null || data.length == 0)
|
||||
return "";
|
||||
|
||||
final char[] out = new char[data.length * 2];
|
||||
for (int j = 0; j < data.length; j++) {
|
||||
int v = data[j] & 0xFF;
|
||||
out[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
out[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return "0x" + new String(out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,11 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -49,7 +52,7 @@ import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
|
||||
* <li>The manager tries to read the Battery Level characteristic. No matter the result of this operation (for example the Battery Level characteristic may not have the READ property)
|
||||
* it tries to enable Battery Level notifications, to get battery updates from the device.</li>
|
||||
* <li>Afterwards, the manager initializes the device using given queue of commands. See {@link BleProfile#initGatt(BluetoothGatt)} method for more details.</li>
|
||||
* <li>When initialization complete, the {@link BleManagerCallbacks#onDeviceReady()} callback is called.</li>
|
||||
* <li>When initialization complete, the {@link BleManagerCallbacks#onDeviceReady(BluetoothDevice)} callback is called.</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
* <p>Events from all profiles are being logged into the nRF Logger application,
|
||||
@@ -59,24 +62,35 @@ import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
|
||||
public class BleManager implements BleProfileApi {
|
||||
private final static String TAG = "BleManager";
|
||||
|
||||
private static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||
private final static UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
private final static UUID BATTERY_SERVICE = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb");
|
||||
private final static UUID BATTERY_LEVEL_CHARACTERISTIC = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
private final static UUID GENERIC_ATTRIBUTE_SERVICE = UUID.fromString("00001801-0000-1000-8000-00805f9b34fb");
|
||||
private final static UUID SERVICE_CHANGED_CHARACTERISTIC = UUID.fromString("00002A05-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
private final static String ERROR_CONNECTION_STATE_CHANGE = "Error on connection state change";
|
||||
private final static String ERROR_DISCOVERY_SERVICE = "Error on discovering services";
|
||||
private final static String ERROR_AUTH_ERROR_WHILE_BONDED = "Phone has lost bonding information";
|
||||
private final static String ERROR_WRITE_DESCRIPTOR = "Error on writing descriptor";
|
||||
private final static String ERROR_READ_CHARACTERISTIC = "Error on reading characteristic";
|
||||
private final Object mLock = new Object();
|
||||
|
||||
protected final BleManagerCallbacks mCallbacks;
|
||||
protected BleProfile mProfile;
|
||||
private final Context mContext;
|
||||
protected BluetoothDevice mBluetoothDevice;
|
||||
protected BleProfile mProfile;
|
||||
private Handler mHandler;
|
||||
private BluetoothGatt mBluetoothGatt;
|
||||
private BleManagerGattCallback mGattCallback;
|
||||
/**
|
||||
* This flag is set to false only when the {@link #shouldAutoConnect()} method returns true and the device got disconnected without calling {@link #disconnect()} method.
|
||||
* If {@link #shouldAutoConnect()} returns false (default) this is always set to true.
|
||||
*/
|
||||
private boolean mUserDisconnected;
|
||||
/**
|
||||
* Flag set to true when the device is connected.
|
||||
*/
|
||||
private boolean mConnected;
|
||||
private int mConnectionState = BluetoothGatt.STATE_DISCONNECTED;
|
||||
/** Last received battery value or -1 if value wasn't received. */
|
||||
private int mBatteryValue = -1;
|
||||
|
||||
private BroadcastReceiver mBondingBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@@ -93,10 +107,10 @@ public class BleManager implements BleProfileApi {
|
||||
|
||||
switch (bondState) {
|
||||
case BluetoothDevice.BOND_BONDING:
|
||||
mCallbacks.onBondingRequired();
|
||||
mCallbacks.onBondingRequired(device);
|
||||
break;
|
||||
case BluetoothDevice.BOND_BONDED:
|
||||
mCallbacks.onBonded();
|
||||
mCallbacks.onBonded(device);
|
||||
|
||||
// Start initializing again.
|
||||
// In fact, bonding forces additional, internal service discovery (at least on Nexus devices), so this method may safely be used to start this process again.
|
||||
@@ -110,7 +124,6 @@ public class BleManager implements BleProfileApi {
|
||||
mCallbacks = callbacks;
|
||||
mContext = context;
|
||||
mHandler = new Handler();
|
||||
mUserDisconnected = false;
|
||||
|
||||
// Register bonding broadcast receiver
|
||||
context.registerReceiver(mBondingBroadcastReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
|
||||
@@ -152,31 +165,64 @@ public class BleManager implements BleProfileApi {
|
||||
if (mConnected)
|
||||
return;
|
||||
|
||||
if (mBluetoothGatt != null) {
|
||||
mBluetoothGatt.close();
|
||||
mBluetoothGatt = null;
|
||||
synchronized (mLock) {
|
||||
if (mBluetoothGatt != null) {
|
||||
mBluetoothGatt.close();
|
||||
mBluetoothGatt = null;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean autoConnect = shouldAutoConnect();
|
||||
mUserDisconnected = !autoConnect; // We will receive Linkloss events only when the device is connected with autoConnect=true
|
||||
mBluetoothGatt = device.connectGatt(mContext, autoConnect, mGattCallback);
|
||||
mBluetoothDevice = device;
|
||||
mConnectionState = BluetoothGatt.STATE_CONNECTING;
|
||||
mBluetoothGatt = device.connectGatt(mContext, autoConnect, mGattCallback = new BleManagerGattCallback());
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects from the device. Does nothing if not connected.
|
||||
*
|
||||
* @return true if device is to be disconnected. False if it was already disconnected.
|
||||
*/
|
||||
public boolean disconnect() {
|
||||
mUserDisconnected = true;
|
||||
|
||||
if (mConnected && mBluetoothGatt != null) {
|
||||
mCallbacks.onDeviceDisconnecting();
|
||||
mConnectionState = BluetoothGatt.STATE_DISCONNECTING;
|
||||
mCallbacks.onDeviceDisconnecting(mBluetoothGatt.getDevice());
|
||||
mBluetoothGatt.disconnect();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns true if the device is connected. Services could have not been discovered yet.
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return mConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns the connection state:
|
||||
* {@link BluetoothGatt#STATE_CONNECTING STATE_CONNECTING},
|
||||
* {@link BluetoothGatt#STATE_CONNECTED STATE_CONNECTED},
|
||||
* {@link BluetoothGatt#STATE_DISCONNECTING STATE_DISCONNECTING},
|
||||
* {@link BluetoothGatt#STATE_DISCONNECTED STATE_DISCONNECTED}
|
||||
* @return the connection state
|
||||
*/
|
||||
public int getConnectionState() {
|
||||
return mConnectionState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last received value of Battery Level characteristic, or -1 if such does not exist, hasn't been read or notification wasn't received yet.
|
||||
* @return the last battery level value in percent
|
||||
*/
|
||||
public int getBatteryValue() {
|
||||
return mBatteryValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes and releases resources. May be also used to unregister broadcast listeners.
|
||||
*/
|
||||
@@ -186,22 +232,27 @@ public class BleManager implements BleProfileApi {
|
||||
} catch (Exception e) {
|
||||
// the receiver must have been not registered or unregistered before
|
||||
}
|
||||
if (mBluetoothGatt != null) {
|
||||
mBluetoothGatt.close();
|
||||
mBluetoothGatt = null;
|
||||
synchronized (mLock) {
|
||||
if (mBluetoothGatt != null) {
|
||||
mBluetoothGatt.close();
|
||||
mBluetoothGatt = null;
|
||||
}
|
||||
mConnected = false;
|
||||
mConnectionState = BluetoothGatt.STATE_DISCONNECTED;
|
||||
mGattCallback = null;
|
||||
mBluetoothDevice = null;
|
||||
}
|
||||
mUserDisconnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the device is bonded and has the Generic Attribute service and the Service Changed characteristic this method enables indications on this characteristic.
|
||||
* In case one of the requirements is not fulfilled this method returns <code>false</code>.
|
||||
*
|
||||
* @param gatt the gatt device with services discovered
|
||||
* @return <code>true</code> when the request has been sent, <code>false</code> when the device is not bonded, does not have the Generic Attribute service, the GA service does not have
|
||||
* the Service Changed characteristic or this characteristic does not have the CCCD.
|
||||
*/
|
||||
private boolean ensureServiceChangedEnabled(final BluetoothGatt gatt) {
|
||||
private boolean ensureServiceChangedEnabled() {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null)
|
||||
return false;
|
||||
|
||||
@@ -223,6 +274,10 @@ public class BleManager implements BleProfileApi {
|
||||
|
||||
@Override
|
||||
public final boolean enableNotifications(final BluetoothGattCharacteristic characteristic) {
|
||||
return enqueue(Request.newEnableNotificationsRequest(characteristic));
|
||||
}
|
||||
|
||||
private boolean internalEnableNotifications(final BluetoothGattCharacteristic characteristic) {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null || characteristic == null)
|
||||
return false;
|
||||
@@ -243,6 +298,10 @@ public class BleManager implements BleProfileApi {
|
||||
|
||||
@Override
|
||||
public final boolean enableIndications(final BluetoothGattCharacteristic characteristic) {
|
||||
return enqueue(Request.newEnableIndicationsRequest(characteristic));
|
||||
}
|
||||
|
||||
private boolean internalEnableIndications(final BluetoothGattCharacteristic characteristic) {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null || characteristic == null)
|
||||
return false;
|
||||
@@ -263,6 +322,10 @@ public class BleManager implements BleProfileApi {
|
||||
|
||||
@Override
|
||||
public final boolean readCharacteristic(final BluetoothGattCharacteristic characteristic) {
|
||||
return enqueue(Request.newReadRequest(characteristic));
|
||||
}
|
||||
|
||||
private boolean internalReadCharacteristic(final BluetoothGattCharacteristic characteristic) {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null || characteristic == null)
|
||||
return false;
|
||||
@@ -277,6 +340,10 @@ public class BleManager implements BleProfileApi {
|
||||
|
||||
@Override
|
||||
public final boolean writeCharacteristic(final BluetoothGattCharacteristic characteristic) {
|
||||
return enqueue(Request.newWriteRequest(characteristic, characteristic.getValue()));
|
||||
}
|
||||
|
||||
private boolean internalWriteCharacteristic(final BluetoothGattCharacteristic characteristic) {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null || characteristic == null)
|
||||
return false;
|
||||
@@ -289,53 +356,132 @@ public class BleManager implements BleProfileApi {
|
||||
return gatt.writeCharacteristic(characteristic);
|
||||
}
|
||||
|
||||
public static final class Request {
|
||||
private enum Type {
|
||||
WRITE,
|
||||
READ,
|
||||
ENABLE_NOTIFICATIONS,
|
||||
ENABLE_INDICATIONS
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final BluetoothGattCharacteristic characteristic;
|
||||
private final byte[] value;
|
||||
|
||||
private Request(final Type type, final BluetoothGattCharacteristic characteristic) {
|
||||
this.type = type;
|
||||
this.characteristic = characteristic;
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattCharacteristic characteristic, final byte[] value) {
|
||||
this.type = type;
|
||||
this.characteristic = characteristic;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Request newReadRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.READ, characteristic);
|
||||
}
|
||||
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] value) {
|
||||
return new Request(Type.WRITE, characteristic, value);
|
||||
}
|
||||
|
||||
public static Request newEnableNotificationsRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.ENABLE_NOTIFICATIONS, characteristic);
|
||||
}
|
||||
|
||||
public static Request newEnableIndicationsRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.ENABLE_INDICATIONS, characteristic);
|
||||
}
|
||||
@Override
|
||||
public final boolean readDescriptor(final BluetoothGattDescriptor descriptor) {
|
||||
return enqueue(Request.newReadRequest(descriptor));
|
||||
}
|
||||
|
||||
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
|
||||
private Queue<Request> mInitQueue;
|
||||
private boolean mInitInProgress;
|
||||
private boolean internalReadDescriptor(final BluetoothGattDescriptor descriptor) {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null || descriptor == null)
|
||||
return false;
|
||||
|
||||
private void onError(final String message, final int errorCode) {
|
||||
mCallbacks.onError(message, errorCode);
|
||||
return gatt.readDescriptor(descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean writeDescriptor(final BluetoothGattDescriptor descriptor) {
|
||||
return enqueue(Request.newWriteRequest(descriptor, descriptor.getValue()));
|
||||
}
|
||||
|
||||
private boolean internalWriteDescriptor(final BluetoothGattDescriptor descriptor) {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null || descriptor == null)
|
||||
return false;
|
||||
|
||||
// There was a bug in Android up to 6.0 where the descriptor was written using parent characteristic write type, instead of always Write With Response,
|
||||
// as the spec says.
|
||||
final BluetoothGattCharacteristic parentCharacteristic = descriptor.getCharacteristic();
|
||||
final int originalWriteType = parentCharacteristic.getWriteType();
|
||||
parentCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
|
||||
final boolean result = gatt.writeDescriptor(descriptor);
|
||||
parentCharacteristic.setWriteType(originalWriteType);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean readBatteryLevel() {
|
||||
return enqueue(Request.newReadBatteryLevelRequest());
|
||||
}
|
||||
|
||||
private boolean internalReadBatteryLevel() {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null)
|
||||
return false;
|
||||
|
||||
final BluetoothGattService batteryService = gatt.getService(BATTERY_SERVICE);
|
||||
if (batteryService == null)
|
||||
return false;
|
||||
|
||||
final BluetoothGattCharacteristic batteryLevelCharacteristic = batteryService.getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC);
|
||||
if (batteryLevelCharacteristic == null)
|
||||
return false;
|
||||
|
||||
// Check characteristic property
|
||||
final int properties = batteryLevelCharacteristic.getProperties();
|
||||
if ((properties & BluetoothGattCharacteristic.PROPERTY_READ) == 0)
|
||||
return false;
|
||||
|
||||
return internalReadCharacteristic(batteryLevelCharacteristic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean setBatteryNotifications(final boolean enable) {
|
||||
if (enable)
|
||||
return enqueue(Request.newEnableBatteryLevelNotificationsRequest());
|
||||
else
|
||||
return enqueue(Request.newDisableBatteryLevelNotificationsRequest());
|
||||
}
|
||||
|
||||
private boolean internalSetBatteryNotifications(final boolean enable) {
|
||||
final BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final BluetoothGattService batteryService = gatt.getService(BATTERY_SERVICE);
|
||||
if (batteryService == null)
|
||||
return false;
|
||||
|
||||
final BluetoothGattCharacteristic batteryLevelCharacteristic = batteryService.getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC);
|
||||
if (batteryLevelCharacteristic == null)
|
||||
return false;
|
||||
|
||||
// Check characteristic property
|
||||
final int properties = batteryLevelCharacteristic.getProperties();
|
||||
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0)
|
||||
return false;
|
||||
|
||||
gatt.setCharacteristicNotification(batteryLevelCharacteristic, enable);
|
||||
final BluetoothGattDescriptor descriptor = batteryLevelCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
|
||||
if (descriptor != null) {
|
||||
if (enable) {
|
||||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
|
||||
} else {
|
||||
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
|
||||
}
|
||||
return gatt.writeDescriptor(descriptor);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enqueue(final Request request) {
|
||||
if (mGattCallback != null) {
|
||||
// Add the new task to the end of the queue
|
||||
mGattCallback.mTaskQueue.add(request);
|
||||
mGattCallback.nextRequest();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private final class BleManagerGattCallback extends BluetoothGattCallback {
|
||||
private final static String ERROR_CONNECTION_STATE_CHANGE = "Error on connection state change";
|
||||
private final static String ERROR_DISCOVERY_SERVICE = "Error on discovering services";
|
||||
private final static String ERROR_AUTH_ERROR_WHILE_BONDED = "Phone has lost bonding information";
|
||||
private final static String ERROR_READ_CHARACTERISTIC = "Error on reading characteristic";
|
||||
private final static String ERROR_WRITE_CHARACTERISTIC = "Error on writing characteristic";
|
||||
private final static String ERROR_READ_DESCRIPTOR = "Error on reading descriptor";
|
||||
private final static String ERROR_WRITE_DESCRIPTOR = "Error on writing descriptor";
|
||||
|
||||
private final Queue<Request> mTaskQueue = new LinkedList<>();
|
||||
private Deque<Request> mInitQueue;
|
||||
private boolean mInitInProgress;
|
||||
private boolean mOperationInProgress;
|
||||
|
||||
private void onError(final BluetoothDevice device, final String message, final int errorCode) {
|
||||
mCallbacks.onError(device, message, errorCode);
|
||||
if (mProfile != null)
|
||||
mProfile.onError(message, errorCode);
|
||||
}
|
||||
@@ -345,7 +491,8 @@ public class BleManager implements BleProfileApi {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
|
||||
// Notify the parent activity/service
|
||||
mConnected = true;
|
||||
mCallbacks.onDeviceConnected();
|
||||
mConnectionState = BluetoothGatt.STATE_CONNECTED;
|
||||
mCallbacks.onDeviceConnected(gatt.getDevice());
|
||||
|
||||
/*
|
||||
* The onConnectionStateChange event is triggered just after the Android connects to a device.
|
||||
@@ -375,11 +522,13 @@ public class BleManager implements BleProfileApi {
|
||||
} else {
|
||||
if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||
mConnected = false;
|
||||
mConnectionState = BluetoothGatt.STATE_DISCONNECTED;
|
||||
mOperationInProgress = true; // no more calls are possible
|
||||
if (mUserDisconnected) {
|
||||
mCallbacks.onDeviceDisconnected();
|
||||
mCallbacks.onDeviceDisconnected(gatt.getDevice());
|
||||
close();
|
||||
} else {
|
||||
mCallbacks.onLinklossOccur();
|
||||
mCallbacks.onLinklossOccur(gatt.getDevice());
|
||||
// We are not closing the connection here as the device should try to reconnect automatically.
|
||||
// This may be only called when the shouldAutoConnect() method returned true.
|
||||
}
|
||||
@@ -405,54 +554,93 @@ public class BleManager implements BleProfileApi {
|
||||
mInitInProgress = true;
|
||||
mInitQueue = profile.initGatt(gatt);
|
||||
|
||||
// When the device is bonded and has Service Changed characteristic, the indications must be enabled first.
|
||||
// In case this method returns true we have to continue in the onDescriptorWrite callback
|
||||
if (ensureServiceChangedEnabled(gatt))
|
||||
return;
|
||||
// Before we start executing the initialization queue some other tasks need to be done.
|
||||
if (mInitQueue == null)
|
||||
mInitQueue = new LinkedList<>();
|
||||
|
||||
// We have discovered services, proceed with the initialization queue.
|
||||
// Note, that operations are added in reverse order to the front of the queue.
|
||||
|
||||
// 3. Enable Battery Level notifications if required (if this char. does not exist, this operation will be skipped)
|
||||
if (mCallbacks.shouldEnableBatteryLevelNotifications(gatt.getDevice()))
|
||||
mInitQueue.addFirst(Request.newEnableBatteryLevelNotificationsRequest());
|
||||
// 2. Read Battery Level characteristic (if such does not exist, this will be skipped)
|
||||
mInitQueue.addFirst(Request.newReadBatteryLevelRequest());
|
||||
// 1. On devices running Android 4.3-6.0 the Service Changed characteristic needs to be enabled by the app (for bonded devices)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
mInitQueue.addFirst(Request.newEnableServiceChangedIndicationsRequest());
|
||||
|
||||
mOperationInProgress = false;
|
||||
nextRequest();
|
||||
} else {
|
||||
mCallbacks.onDeviceNotSupported();
|
||||
mCallbacks.onDeviceNotSupported(gatt.getDevice());
|
||||
disconnect();
|
||||
}
|
||||
} else {
|
||||
DebugLogger.e(TAG, "onServicesDiscovered error " + status);
|
||||
onError(ERROR_DISCOVERY_SERVICE, status);
|
||||
onError(gatt.getDevice(), ERROR_DISCOVERY_SERVICE, status);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// The value has been read. Notify the profile and proceed with the initialization queue.
|
||||
mProfile.onCharacteristicRead(gatt, characteristic);
|
||||
if (isBatteryLevelCharacteristic(characteristic)) {
|
||||
final int batteryValue = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
|
||||
mBatteryValue = batteryValue;
|
||||
mProfile.onBatteryValueReceived(gatt, batteryValue);
|
||||
} else {
|
||||
// The value has been read. Notify the profile and proceed with the initialization queue.
|
||||
mProfile.onCharacteristicRead(gatt, characteristic);
|
||||
}
|
||||
mOperationInProgress = false;
|
||||
nextRequest();
|
||||
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
|
||||
if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
|
||||
// This should never happen but it used to: http://stackoverflow.com/a/20093695/2115352
|
||||
DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED);
|
||||
onError(ERROR_AUTH_ERROR_WHILE_BONDED, status);
|
||||
onError(gatt.getDevice(), ERROR_AUTH_ERROR_WHILE_BONDED, status);
|
||||
}
|
||||
} else {
|
||||
DebugLogger.e(TAG, "onCharacteristicRead error " + status);
|
||||
onError(ERROR_READ_CHARACTERISTIC, status);
|
||||
onError(gatt.getDevice(), ERROR_READ_CHARACTERISTIC, status);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
|
||||
public final void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// The value has been written. Notify the profile and proceed with the initialization queue.
|
||||
mProfile.onCharacteristicWrite(gatt, characteristic);
|
||||
mOperationInProgress = false;
|
||||
nextRequest();
|
||||
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
|
||||
if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
|
||||
// This should never happen but it used to: http://stackoverflow.com/a/20093695/2115352
|
||||
DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED);
|
||||
onError(ERROR_AUTH_ERROR_WHILE_BONDED, status);
|
||||
onError(gatt.getDevice(), ERROR_AUTH_ERROR_WHILE_BONDED, status);
|
||||
}
|
||||
} else {
|
||||
DebugLogger.e(TAG, "onCharacteristicRead error " + status);
|
||||
onError(ERROR_READ_CHARACTERISTIC, status);
|
||||
DebugLogger.e(TAG, "onCharacteristicWrite error " + status);
|
||||
onError(gatt.getDevice(), ERROR_WRITE_CHARACTERISTIC, status);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorRead(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// The value has been read. Notify the profile and proceed with the initialization queue.
|
||||
mProfile.onDescriptorRead(gatt, descriptor);
|
||||
mOperationInProgress = false;
|
||||
nextRequest();
|
||||
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
|
||||
if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
|
||||
// This should never happen but it used to: http://stackoverflow.com/a/20093695/2115352
|
||||
DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED);
|
||||
onError(gatt.getDevice(), ERROR_AUTH_ERROR_WHILE_BONDED, status);
|
||||
}
|
||||
} else {
|
||||
DebugLogger.e(TAG, "onDescriptorRead error " + status);
|
||||
onError(gatt.getDevice(), ERROR_READ_DESCRIPTOR, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,68 +649,132 @@ public class BleManager implements BleProfileApi {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// The value has been written. Notify the profile and proceed with the initialization queue.
|
||||
mProfile.onDescriptorWrite(gatt, descriptor);
|
||||
mOperationInProgress = false;
|
||||
nextRequest();
|
||||
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
|
||||
if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
|
||||
// This should never happen but it used to: http://stackoverflow.com/a/20093695/2115352
|
||||
DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED);
|
||||
onError(ERROR_AUTH_ERROR_WHILE_BONDED, status);
|
||||
onError(gatt.getDevice(), ERROR_AUTH_ERROR_WHILE_BONDED, status);
|
||||
}
|
||||
} else {
|
||||
DebugLogger.e(TAG, "onDescriptorWrite error " + status);
|
||||
onError(ERROR_WRITE_DESCRIPTOR, status);
|
||||
onError(gatt.getDevice(), ERROR_WRITE_DESCRIPTOR, status);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
final BluetoothGattDescriptor cccd = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
|
||||
final boolean notifications = cccd == null || cccd.getValue() == null || cccd.getValue().length != 2 || cccd.getValue()[0] == 0x01;
|
||||
if (isBatteryLevelCharacteristic(characteristic)) {
|
||||
final int batteryValue = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
|
||||
mBatteryValue = batteryValue;
|
||||
mProfile.onBatteryValueReceived(gatt, batteryValue);
|
||||
} else {
|
||||
final BluetoothGattDescriptor cccd = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
|
||||
final boolean notifications = cccd == null || cccd.getValue() == null || cccd.getValue().length != 2 || cccd.getValue()[0] == 0x01;
|
||||
|
||||
if (notifications) {
|
||||
mProfile.onCharacteristicNotified(gatt, characteristic);
|
||||
} else { // indications
|
||||
mProfile.onCharacteristicIndicated(gatt, characteristic);
|
||||
if (notifications) {
|
||||
mProfile.onCharacteristicNotified(gatt, characteristic);
|
||||
} else { // indications
|
||||
mProfile.onCharacteristicIndicated(gatt, characteristic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the next initialization request. If the last element from the queue has been executed a {@link BleManagerCallbacks#onDeviceReady()} callback is called.
|
||||
* Executes the next request. If the last element from the initialization queue has been executed
|
||||
* the {@link BleManagerCallbacks#onDeviceReady(BluetoothDevice)} callback is called.
|
||||
*/
|
||||
private void nextRequest() {
|
||||
final Queue<Request> requests = mInitQueue;
|
||||
if (mOperationInProgress)
|
||||
return;
|
||||
|
||||
// Get the first request from the queue
|
||||
final Request request = requests != null ? requests.poll() : null;
|
||||
// Get the first request from the init queue
|
||||
Request request = mInitQueue != null ? mInitQueue.poll() : null;
|
||||
|
||||
// Are we done?
|
||||
// Are we done with initializing?
|
||||
if (request == null) {
|
||||
if (mInitInProgress) {
|
||||
mInitQueue = null; // release the queue
|
||||
mInitInProgress = false;
|
||||
mCallbacks.onDeviceReady();
|
||||
mCallbacks.onDeviceReady(mBluetoothDevice);
|
||||
}
|
||||
// If so, we can continue with the task queue
|
||||
request = mTaskQueue.poll();
|
||||
if (request == null) {
|
||||
// Nothing to be done for now
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mOperationInProgress = true;
|
||||
boolean result = false;
|
||||
switch (request.type) {
|
||||
case READ: {
|
||||
readCharacteristic(request.characteristic);
|
||||
result = internalReadCharacteristic(request.characteristic);
|
||||
break;
|
||||
}
|
||||
case WRITE: {
|
||||
final BluetoothGattCharacteristic characteristic = request.characteristic;
|
||||
characteristic.setValue(request.value);
|
||||
writeCharacteristic(characteristic);
|
||||
characteristic.setWriteType(request.writeType);
|
||||
result = internalWriteCharacteristic(characteristic);
|
||||
break;
|
||||
}
|
||||
case READ_DESCRIPTOR: {
|
||||
result = internalReadDescriptor(request.descriptor);
|
||||
break;
|
||||
}
|
||||
case WRITE_DESCRIPTOR: {
|
||||
final BluetoothGattDescriptor descriptor = request.descriptor;
|
||||
descriptor.setValue(request.value);
|
||||
result = internalWriteDescriptor(descriptor);
|
||||
break;
|
||||
}
|
||||
case ENABLE_NOTIFICATIONS: {
|
||||
enableNotifications(request.characteristic);
|
||||
result = internalEnableNotifications(request.characteristic);
|
||||
break;
|
||||
}
|
||||
case ENABLE_INDICATIONS: {
|
||||
enableIndications(request.characteristic);
|
||||
result = internalEnableIndications(request.characteristic);
|
||||
break;
|
||||
}
|
||||
case READ_BATTERY_LEVEL: {
|
||||
result = internalReadBatteryLevel();
|
||||
break;
|
||||
}
|
||||
case ENABLE_BATTERY_LEVEL_NOTIFICATIONS: {
|
||||
result = internalSetBatteryNotifications(true);
|
||||
break;
|
||||
}
|
||||
case DISABLE_BATTERY_LEVEL_NOTIFICATIONS: {
|
||||
result = internalSetBatteryNotifications(false);
|
||||
break;
|
||||
}
|
||||
case ENABLE_SERVICE_CHANGED_INDICATIONS: {
|
||||
result = ensureServiceChangedEnabled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The result may be false if given characteristic or descriptor were not found on the device.
|
||||
// In that case, proceed with next operation and ignore the one that failed.
|
||||
if (!result) {
|
||||
mOperationInProgress = false;
|
||||
nextRequest();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the characteristic is the Battery Level characteristic.
|
||||
*
|
||||
* @param characteristic the characteristic to be checked
|
||||
* @return true if the characteristic is the Battery Level characteristic.
|
||||
*/
|
||||
private boolean isBatteryLevelCharacteristic(final BluetoothGattCharacteristic characteristic) {
|
||||
if (characteristic == null)
|
||||
return false;
|
||||
|
||||
return BATTERY_LEVEL_CHARACTERISTIC.equals(characteristic.getUuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,60 +21,91 @@
|
||||
*/
|
||||
package no.nordicsemi.android.nrftoolbox.ble;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
|
||||
public interface BleManagerCallbacks {
|
||||
|
||||
/**
|
||||
* Called when the device has been connected. This does not mean that the application may start communication. A service discovery will be handled automatically after this call. Service discovery
|
||||
* may ends up with calling {@link #onDeviceReady()} or {@link #onDeviceNotSupported()} if required services have not been found.
|
||||
* Called when the Android device started connecting to given device.
|
||||
* The {@link #onDeviceConnected(BluetoothDevice)} will be called when the device is connected,
|
||||
* or {@link #onError(BluetoothDevice, String, int)} in case of error.
|
||||
* @param device the device that got connected
|
||||
*/
|
||||
public void onDeviceConnected();
|
||||
void onDeviceConnecting(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when the device has been connected. This does not mean that the application may start communication.
|
||||
* A service discovery will be handled automatically after this call. Service discovery
|
||||
* may ends up with calling {@link #onDeviceReady(BluetoothDevice)}
|
||||
* or {@link #onDeviceNotSupported(BluetoothDevice)} if required services have not been found.
|
||||
* @param device target device
|
||||
*/
|
||||
void onDeviceConnected(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Method called when all initialization requests has been completed.
|
||||
* @param device target device
|
||||
*/
|
||||
public void onDeviceReady();
|
||||
void onDeviceReady(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* This method should return true if Battery Level notifications should be enabled on the target device.
|
||||
* If there is no Battery Service, or the Battery Level characteristic does not have NOTIFY property,
|
||||
* this method will not be called for this device.
|
||||
* <p>This method may return true only if an activity is bound to the service (to display the information
|
||||
* to the user), always (e.g. if critical battery level is reported using notifications) or never, if
|
||||
* such information is not important or the manager wants to control Battery Level notifications on its own.</p>
|
||||
* @param device target device
|
||||
* @return true to enabled battery level notifications after connecting to the device, false otherwise
|
||||
*/
|
||||
boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when user initialized disconnection.
|
||||
* @param device target device
|
||||
*/
|
||||
public void onDeviceDisconnecting();
|
||||
void onDeviceDisconnecting(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when the device has disconnected (when the callback returned {@link BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)} with state DISCONNECTED.
|
||||
* Called when the device has disconnected (when the callback returned
|
||||
* {@link BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)} with state DISCONNECTED.
|
||||
* @param device target device
|
||||
*/
|
||||
public void onDeviceDisconnected();
|
||||
void onDeviceDisconnected(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* This callback is invoked when the Ble Manager lost connection to a device that has been connected with autoConnect option. Otherwise a {@link #onDeviceDisconnected()}
|
||||
* method will be called on such event.
|
||||
* This callback is invoked when the Ble Manager lost connection to a device that has been connected with autoConnect option.
|
||||
* Otherwise a {@link #onDeviceDisconnected(BluetoothDevice)} method will be called on such event.
|
||||
* @param device target device
|
||||
*/
|
||||
public void onLinklossOccur();
|
||||
void onLinklossOccur(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when an {@link BluetoothGatt#GATT_INSUFFICIENT_AUTHENTICATION} error occurred and the device bond state is NOT_BONDED
|
||||
* @param device target device
|
||||
*/
|
||||
public void onBondingRequired();
|
||||
void onBondingRequired(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when the device has been successfully bonded.
|
||||
* @param device target device
|
||||
*/
|
||||
public void onBonded();
|
||||
void onBonded(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when a BLE error has occurred
|
||||
*
|
||||
* @param message
|
||||
* the error message
|
||||
* @param errorCode
|
||||
* the error code
|
||||
*
|
||||
* @param device target device
|
||||
* @param message the error message
|
||||
* @param errorCode the error code
|
||||
*/
|
||||
public void onError(final String message, final int errorCode);
|
||||
void onError(final BluetoothDevice device, final String message, final int errorCode);
|
||||
|
||||
/**
|
||||
* Called when service discovery has finished but the main services were not found on the device.
|
||||
* @param device target device
|
||||
*/
|
||||
public void onDeviceNotSupported();
|
||||
void onDeviceNotSupported(final BluetoothDevice device);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.Deque;
|
||||
|
||||
public abstract class BleProfile {
|
||||
private Context mContext;
|
||||
@@ -63,19 +63,28 @@ public abstract class BleProfile {
|
||||
* @param gatt the gatt device with services discovered
|
||||
* @return the queue of requests
|
||||
*/
|
||||
protected abstract Queue<BleManager.Request> initGatt(final BluetoothGatt gatt);
|
||||
protected abstract Deque<BleManager.Request> initGatt(final BluetoothGatt gatt);
|
||||
|
||||
/**
|
||||
* Releases all profile resources. The device is no longer connected.
|
||||
*/
|
||||
protected abstract void release();
|
||||
|
||||
/**
|
||||
* Called when battery value has been received from the device.
|
||||
*
|
||||
* @param gatt GATT client
|
||||
* @param value the battery value in percent
|
||||
*/
|
||||
protected void onBatteryValueReceived(final BluetoothGatt gatt, final int value) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback reporting the result of a characteristic read operation.
|
||||
*
|
||||
* @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
|
||||
* @param characteristic Characteristic that was read from the associated
|
||||
* remote device.
|
||||
* @param gatt GATT client
|
||||
* @param characteristic Characteristic that was read from the associated remote device.
|
||||
*/
|
||||
protected void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// do nothing
|
||||
@@ -83,40 +92,65 @@ public abstract class BleProfile {
|
||||
|
||||
/**
|
||||
* Callback indicating the result of a characteristic write operation.
|
||||
* <p/>
|
||||
* <p>If this callback is invoked while a reliable write transaction is
|
||||
* in progress, the value of the characteristic represents the value
|
||||
* reported by the remote device. An application should compare this
|
||||
* value to the desired value to be written. If the values don't match,
|
||||
* the application must abort the reliable write transaction.
|
||||
*
|
||||
* @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
|
||||
* @param characteristic Characteristic that was written to the associated
|
||||
* remote device.
|
||||
* @param gatt GATT client
|
||||
* @param characteristic Characteristic that was written to the associated remote device.
|
||||
*/
|
||||
protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback reporting the result of a descriptor read operation.
|
||||
*
|
||||
* @param gatt GATT client
|
||||
* @param descriptor Descriptor that was read from the associated remote device.
|
||||
*/
|
||||
protected void onDescriptorRead(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback indicating the result of a descriptor write operation.
|
||||
* <p>If this callback is invoked while a reliable write transaction is in progress,
|
||||
* the value of the characteristic represents the value reported by the remote device.
|
||||
* An application should compare this value to the desired value to be written.
|
||||
* If the values don't match, the application must abort the reliable write transaction.
|
||||
*
|
||||
* @param gatt GATT client
|
||||
* @param descriptor Descriptor that was written to the associated remote device.
|
||||
*/
|
||||
protected void onDescriptorWrite(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback indicating a notification has been received.
|
||||
* @param gatt GATT client
|
||||
* @param characteristic Characteristic from which the notification came.
|
||||
*/
|
||||
protected void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback indicating an indication has been received.
|
||||
* @param gatt GATT client
|
||||
* @param characteristic Characteristic from which the indication came.
|
||||
*/
|
||||
protected void onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a BLE error has occurred
|
||||
*
|
||||
* @param message
|
||||
* the error message
|
||||
* @param errorCode
|
||||
* the error code
|
||||
* @param message the error message
|
||||
* @param errorCode the error code
|
||||
*/
|
||||
public void onError(final String message, final int errorCode) {
|
||||
// do nothing
|
||||
|
||||
@@ -23,41 +23,312 @@
|
||||
package no.nordicsemi.android.nrftoolbox.ble;
|
||||
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.content.Context;
|
||||
|
||||
public interface BleProfileApi {
|
||||
|
||||
/**
|
||||
* On Android, when multiple BLE operations needs to be done, it is required to wait for a proper
|
||||
* {@link android.bluetooth.BluetoothGattCallback BluetoothGattCallback} callback before calling
|
||||
* another operation. In order to make BLE operations easier the BleManager allows to enqueue a request
|
||||
* containing all data necessary for a given operation. Requests are performed one after another until the
|
||||
* queue is empty. Use static methods from below to instantiate a request and then enqueue them using {@link #enqueue(Request)}.
|
||||
*/
|
||||
final class Request {
|
||||
enum Type {
|
||||
WRITE,
|
||||
READ,
|
||||
WRITE_DESCRIPTOR,
|
||||
READ_DESCRIPTOR,
|
||||
ENABLE_NOTIFICATIONS,
|
||||
ENABLE_INDICATIONS,
|
||||
READ_BATTERY_LEVEL,
|
||||
ENABLE_BATTERY_LEVEL_NOTIFICATIONS,
|
||||
DISABLE_BATTERY_LEVEL_NOTIFICATIONS,
|
||||
ENABLE_SERVICE_CHANGED_INDICATIONS,
|
||||
}
|
||||
|
||||
final Type type;
|
||||
final BluetoothGattCharacteristic characteristic;
|
||||
final BluetoothGattDescriptor descriptor;
|
||||
final byte[] value;
|
||||
final int writeType;
|
||||
|
||||
private Request(final Type type) {
|
||||
this.type = type;
|
||||
this.characteristic = null;
|
||||
this.descriptor = null;
|
||||
this.value = null;
|
||||
this.writeType = 0;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattCharacteristic characteristic) {
|
||||
this.type = type;
|
||||
this.characteristic = characteristic;
|
||||
this.descriptor = null;
|
||||
this.value = null;
|
||||
this.writeType = 0;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattCharacteristic characteristic, final int writeType, final byte[] value, final int offset, final int length) {
|
||||
this.type = type;
|
||||
this.characteristic = characteristic;
|
||||
this.descriptor = null;
|
||||
this.value = copy(value, offset, length);
|
||||
this.writeType = writeType;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattDescriptor descriptor) {
|
||||
this.type = type;
|
||||
this.characteristic = null;
|
||||
this.descriptor = descriptor;
|
||||
this.value = null;
|
||||
this.writeType = 0;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattDescriptor descriptor, final byte[] value, final int offset, final int length) {
|
||||
this.type = type;
|
||||
this.characteristic = null;
|
||||
this.descriptor = descriptor;
|
||||
this.value = copy(value, offset, length);
|
||||
this.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
private static byte[] copy(final byte[] value, final int offset, final int length) {
|
||||
if (value == null || offset > value.length)
|
||||
return null;
|
||||
final int maxLength = Math.min(value.length - offset, length);
|
||||
final byte[] copy = new byte[maxLength];
|
||||
System.arraycopy(value, offset, copy, 0, maxLength);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Read Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have READ property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be read
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newReadRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.READ, characteristic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have WRITE property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be written
|
||||
* @param value value to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] value) {
|
||||
return new Request(Type.WRITE, characteristic, characteristic.getWriteType(), value, 0, value != null ? value.length : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have WRITE property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be written
|
||||
* @param value value to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @param writeType write type to be used, one of {@link BluetoothGattCharacteristic#WRITE_TYPE_DEFAULT}, {@link BluetoothGattCharacteristic#WRITE_TYPE_NO_RESPONSE}.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] value, final int writeType) {
|
||||
return new Request(Type.WRITE, characteristic, writeType, value, 0, value != null ? value.length : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have WRITE property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be written
|
||||
* @param value value to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @param offset the offset from which value has to be copied
|
||||
* @param length number of bytes to be copied from the value buffer
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] value, final int offset, final int length) {
|
||||
return new Request(Type.WRITE, characteristic, characteristic.getWriteType(), value, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have WRITE property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be written
|
||||
* @param value value to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @param offset the offset from which value has to be copied
|
||||
* @param length number of bytes to be copied from the value buffer
|
||||
* @param writeType write type to be used, one of {@link BluetoothGattCharacteristic#WRITE_TYPE_DEFAULT}, {@link BluetoothGattCharacteristic#WRITE_TYPE_NO_RESPONSE}.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] value, final int offset, final int length, final int writeType) {
|
||||
return new Request(Type.WRITE, characteristic, writeType, value, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Read Descriptor request. The request will not be executed if given descriptor
|
||||
* is null. After the operation is complete a proper callback will be invoked.
|
||||
* @param descriptor descriptor to be read
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newReadRequest(final BluetoothGattDescriptor descriptor) {
|
||||
return new Request(Type.READ_DESCRIPTOR, descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Descriptor request. The request will not be executed if given descriptor
|
||||
* is null. After the operation is complete a proper callback will be invoked.
|
||||
* @param descriptor descriptor to be written
|
||||
* @param value value to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattDescriptor descriptor, final byte[] value) {
|
||||
return new Request(Type.WRITE_DESCRIPTOR, descriptor, value, 0, value != null ? value.length : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Descriptor request. The request will not be executed if given descriptor
|
||||
* is null. After the operation is complete a proper callback will be invoked.
|
||||
* @param descriptor descriptor to be written
|
||||
* @param value value to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @param offset the offset from which value has to be copied
|
||||
* @param length number of bytes to be copied from the value buffer
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattDescriptor descriptor, final byte[] value, final int offset, final int length) {
|
||||
return new Request(Type.WRITE_DESCRIPTOR, descriptor, value, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Enable Notification request. The request will not be executed if given characteristic
|
||||
* is null, does not have NOTIFY property or the CCCD. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to have notifications enabled
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newEnableNotificationsRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.ENABLE_NOTIFICATIONS, characteristic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Enable Indications request. The request will not be executed if given characteristic
|
||||
* is null, does not have INDICATE property or the CCCD. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to have indications enabled
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newEnableIndicationsRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.ENABLE_INDICATIONS, characteristic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the first found Battery Level characteristic value from the first found Battery Service.
|
||||
* If any of them is not found, or the characteristic does not have the READ property this operation will not execute.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newReadBatteryLevelRequest() {
|
||||
return new Request(Type.READ_BATTERY_LEVEL); // the first Battery Level char from the first Battery Service is used
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables notifications on the first found Battery Level characteristic from the first found Battery Service.
|
||||
* If any of them is not found, or the characteristic does not have the NOTIFY property this operation will not execute.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newEnableBatteryLevelNotificationsRequest() {
|
||||
return new Request(Type.ENABLE_BATTERY_LEVEL_NOTIFICATIONS); // the first Battery Level char from the first Battery Service is used
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables notifications on the first found Battery Level characteristic from the first found Battery Service.
|
||||
* If any of them is not found, or the characteristic does not have the NOTIFY property this operation will not execute.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newDisableBatteryLevelNotificationsRequest() {
|
||||
return new Request(Type.DISABLE_BATTERY_LEVEL_NOTIFICATIONS); // the first Battery Level char from the first Battery Service is used
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables indications on Service Changed characteristic if such exists in the Generic Attribute service.
|
||||
* It is required to enable those notifications on bonded devices on older Android versions to be
|
||||
* informed about attributes changes. Android 7+ (or 6+) handles this automatically and no action is required.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
static Request newEnableServiceChangedIndicationsRequest() {
|
||||
return new Request(Type.ENABLE_SERVICE_CHANGED_INDICATIONS); // the only Service Changed char is used (if such exists)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context.
|
||||
*/
|
||||
public Context getContext();
|
||||
Context getContext();
|
||||
|
||||
/**
|
||||
* Enables notifications on given characteristic
|
||||
*
|
||||
* @return true is the request has been sent, false if one of the arguments was <code>null</code> or the characteristic does not have the CCCD.
|
||||
* @return true is the request has been enqueued
|
||||
*/
|
||||
public boolean enableNotifications(final BluetoothGattCharacteristic characteristic);
|
||||
boolean enableNotifications(final BluetoothGattCharacteristic characteristic);
|
||||
|
||||
/**
|
||||
* Enables indications on given characteristic
|
||||
*
|
||||
* @return true is the request has been sent, false if one of the arguments was <code>null</code> or the characteristic does not have the CCCD.
|
||||
* @return true is the request has been enqueued
|
||||
*/
|
||||
public boolean enableIndications(final BluetoothGattCharacteristic characteristic);
|
||||
boolean enableIndications(final BluetoothGattCharacteristic characteristic);
|
||||
|
||||
/**
|
||||
* Sends the read request to the given characteristic.
|
||||
*
|
||||
* @param characteristic the characteristic to read
|
||||
* @return true if request has been sent
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
public boolean readCharacteristic(final BluetoothGattCharacteristic characteristic);
|
||||
boolean readCharacteristic(final BluetoothGattCharacteristic characteristic);
|
||||
|
||||
/**
|
||||
* Writes the characteristic value to the given characteristic.
|
||||
*
|
||||
* @param characteristic the characteristic to write to
|
||||
* @return true if request has been sent
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
public boolean writeCharacteristic(final BluetoothGattCharacteristic characteristic);
|
||||
boolean writeCharacteristic(final BluetoothGattCharacteristic characteristic);
|
||||
|
||||
/**
|
||||
* Sends the read request to the given descriptor.
|
||||
*
|
||||
* @param descriptor the descriptor to read
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean readDescriptor(final BluetoothGattDescriptor descriptor);
|
||||
|
||||
/**
|
||||
* Writes the descriptor value to the given descriptor.
|
||||
*
|
||||
* @param descriptor the descriptor to write to
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean writeDescriptor(final BluetoothGattDescriptor descriptor);
|
||||
|
||||
/**
|
||||
* Reads the battery level from the device.
|
||||
*
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean readBatteryLevel();
|
||||
|
||||
/**
|
||||
* This method tries to enable notifications on the Battery Level characteristic.
|
||||
*
|
||||
* @param enable <code>true</code> to enable battery notifications, false to disable
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean setBatteryNotifications(final boolean enable);
|
||||
|
||||
/**
|
||||
* Enqueues a new request. The request will be handled immediately if there is no operation in progress,
|
||||
* or automatically after the last enqueued one will finish.
|
||||
* <p>This method should be used to read and write data from the target device as it ensures that the last operation has finished
|
||||
* before a new one will be called.</p>
|
||||
* @param request new request to be performed
|
||||
* @return true if request has been enqueued, false if the device is not connected
|
||||
*/
|
||||
boolean enqueue(final Request request);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import android.app.Service;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Binder;
|
||||
@@ -41,12 +43,14 @@ public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
public static final String BROADCAST_DEVICE_NOT_SUPPORTED = "no.nordicsemi.android.nrftoolbox.BROADCAST_DEVICE_NOT_SUPPORTED";
|
||||
public static final String BROADCAST_DEVICE_READY = "no.nordicsemi.android.nrftoolbox.DEVICE_READY";
|
||||
public static final String BROADCAST_BOND_STATE = "no.nordicsemi.android.nrftoolbox.BROADCAST_BOND_STATE";
|
||||
public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
|
||||
public static final String BROADCAST_ERROR = "no.nordicsemi.android.nrftoolbox.BROADCAST_ERROR";
|
||||
|
||||
/** The parameter passed when creating the service. Must contain the address of the sensor that we want to connect to */
|
||||
public static final String EXTRA_DEVICE_ADDRESS = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE_ADDRESS";
|
||||
/** The key for the device name that is returned in {@link #BROADCAST_CONNECTION_STATE} with state {@link #STATE_CONNECTED}. */
|
||||
public static final String EXTRA_DEVICE_NAME = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE_NAME";
|
||||
public static final String EXTRA_DEVICE = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE";
|
||||
public static final String EXTRA_CONNECTION_STATE = "no.nordicsemi.android.nrftoolbox.EXTRA_CONNECTION_STATE";
|
||||
public static final String EXTRA_BOND_STATE = "no.nordicsemi.android.nrftoolbox.EXTRA_BOND_STATE";
|
||||
public static final String EXTRA_ERROR_MESSAGE = "no.nordicsemi.android.nrftoolbox.EXTRA_ERROR_MESSAGE";
|
||||
@@ -63,17 +67,34 @@ public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
|
||||
protected boolean mBinded;
|
||||
private boolean mConnected;
|
||||
private String mDeviceAddress;
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
private String mDeviceName;
|
||||
|
||||
private final BroadcastReceiver mBluetoothStateBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
|
||||
|
||||
switch (state) {
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
onBluetoothEnabled();
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
onBluetoothDisabled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public class LocalBinder extends Binder {
|
||||
/**
|
||||
* Disconnects from the sensor.
|
||||
*/
|
||||
public final void disconnect() {
|
||||
public void disconnect() {
|
||||
if (!mConnected) {
|
||||
mBleManager.close();
|
||||
onDeviceDisconnected();
|
||||
onDeviceDisconnected(mBluetoothDevice);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -85,8 +106,8 @@ public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
*
|
||||
* @return device address
|
||||
*/
|
||||
public String getDeviceAddress() {
|
||||
return mDeviceAddress;
|
||||
public final String getDeviceAddress() {
|
||||
return mBluetoothDevice.getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,29 +115,38 @@ public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
*
|
||||
* @return the device name
|
||||
*/
|
||||
public String getDeviceName() {
|
||||
public final String getDeviceName() {
|
||||
return mDeviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Bluetooth device
|
||||
*
|
||||
* @return the Bluetooth device
|
||||
*/
|
||||
public final BluetoothDevice getBluetoothDevice() {
|
||||
return mBluetoothDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the device is connected to the sensor.
|
||||
*
|
||||
* @return <code>true</code> if device is connected to the sensor, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
public final boolean isConnected() {
|
||||
return mConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Profile API. Profile may be null if service discovery has not been performed or the device does not match any profile.
|
||||
*/
|
||||
public BleProfile getProfile() {
|
||||
public final BleProfile getProfile() {
|
||||
return mBleManager.getProfile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the binder implementation. This must return class implementing the additional manager interface that may be used in the binded activity.
|
||||
* Returns the binder implementation. This must return class implementing the additional manager interface that may be used in the bound activity.
|
||||
*
|
||||
* @return the service binder
|
||||
*/
|
||||
@@ -140,7 +170,7 @@ public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
public final boolean onUnbind(final Intent intent) {
|
||||
mBinded = false;
|
||||
|
||||
// we must allow to rebind to the same service
|
||||
// We want the onRebind method be called if anything else binds to it again
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -153,6 +183,25 @@ public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
|
||||
// initialize the manager
|
||||
mBleManager = new BleManager(this, this);
|
||||
|
||||
// Register broadcast receivers
|
||||
registerReceiver(mBluetoothStateBroadcastReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
||||
|
||||
// Service has now been created
|
||||
onServiceCreated();
|
||||
|
||||
// Call onBluetoothEnabled if Bluetooth enabled
|
||||
final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter.isEnabled()) {
|
||||
onBluetoothEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the service has been created, before the {@link #onBluetoothEnabled()} is called.
|
||||
*/
|
||||
protected void onServiceCreated() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -160,58 +209,92 @@ public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
if (intent == null || !intent.hasExtra(EXTRA_DEVICE_ADDRESS))
|
||||
throw new UnsupportedOperationException("No device address at EXTRA_DEVICE_ADDRESS key");
|
||||
|
||||
mDeviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
|
||||
|
||||
// notify user about changing the state to CONNECTING
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTING);
|
||||
LocalBroadcastManager.getInstance(BleProfileService.this).sendBroadcast(broadcast);
|
||||
mDeviceName = intent.getStringExtra(EXTRA_DEVICE_NAME);
|
||||
|
||||
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
|
||||
final BluetoothAdapter adapter = bluetoothManager.getAdapter();
|
||||
final BluetoothDevice device = adapter.getRemoteDevice(mDeviceAddress);
|
||||
mDeviceName = device.getName();
|
||||
final String deviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
|
||||
mBluetoothDevice = adapter.getRemoteDevice(deviceAddress);
|
||||
onServiceStarted();
|
||||
|
||||
mBleManager.connect(device);
|
||||
mBleManager.connect(mBluetoothDevice);
|
||||
return START_REDELIVER_INTENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the service has been started. The device name and address are set. It nRF Logger is installed than logger was also initialized.
|
||||
*/
|
||||
protected void onServiceStarted() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Unregister broadcast receivers
|
||||
unregisterReceiver(mBluetoothStateBroadcastReceiver);
|
||||
|
||||
// shutdown the manager
|
||||
mBleManager.close();
|
||||
mBleManager = null;
|
||||
mDeviceAddress = null;
|
||||
mBluetoothDevice = null;
|
||||
mDeviceName = null;
|
||||
mConnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when Bluetooth Adapter has been disabled.
|
||||
*/
|
||||
protected void onBluetoothDisabled() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when Bluetooth Adapter has been enabled and
|
||||
* after the service was created if Bluetooth Adapter was enabled at that moment.
|
||||
* This method could initialize all Bluetooth related features, for example open the GATT server.
|
||||
*/
|
||||
protected void onBluetoothEnabled() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnected() {
|
||||
public boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// By default the Battery Level notifications will be enabled only the activity is bound.
|
||||
return mBinded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnecting(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTING);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnected(final BluetoothDevice device) {
|
||||
mConnected = true;
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTED);
|
||||
broadcast.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_DEVICE_NAME, mDeviceName);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnecting() {
|
||||
public void onDeviceDisconnecting(final BluetoothDevice device) {
|
||||
// Notify user about changing the state to DISCONNECTING
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTING);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnected() {
|
||||
public void onDeviceDisconnected(final BluetoothDevice device) {
|
||||
mConnected = false;
|
||||
mDeviceAddress = null;
|
||||
mDeviceName = null;
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTED);
|
||||
@@ -221,53 +304,61 @@ public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinklossOccur() {
|
||||
public void onLinklossOccur(final BluetoothDevice device) {
|
||||
mConnected = false;
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_LINK_LOSS);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceReady() {
|
||||
public void onDeviceReady(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_DEVICE_READY);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceNotSupported() {
|
||||
public void onDeviceNotSupported(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_DEVICE_NOT_SUPPORTED);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
|
||||
// no need for disconnecting, it will be disconnected by the manager automatically
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBondingRequired() {
|
||||
public void onBondingRequired(final BluetoothDevice device) {
|
||||
showToast(no.nordicsemi.android.nrftoolbox.common.R.string.bonding);
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_BOND_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBonded() {
|
||||
public void onBonded(final BluetoothDevice device) {
|
||||
showToast(no.nordicsemi.android.nrftoolbox.common.R.string.bonded);
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_BOND_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final String message, final int errorCode) {
|
||||
public void onError(final BluetoothDevice device, final String message, final int errorCode) {
|
||||
final Intent broadcast = new Intent(BROADCAST_ERROR);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_ERROR_MESSAGE, message);
|
||||
broadcast.putExtra(EXTRA_ERROR_CODE, errorCode);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
|
||||
// After receiving an error the device will be automatically disconnected.
|
||||
// Replace it with other implementation if necessary.
|
||||
mBleManager.disconnect();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@@ -27,11 +27,12 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.Deque;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleProfile;
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleProfileApi;
|
||||
|
||||
public class UARTProfile extends BleProfile {
|
||||
/** Broadcast sent when a UART message is received. */
|
||||
@@ -40,11 +41,11 @@ public class UARTProfile extends BleProfile {
|
||||
public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.EXTRA_DATA";
|
||||
|
||||
/** Nordic UART Service UUID */
|
||||
private final static UUID UART_SERVICE_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
private static final UUID UART_SERVICE_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
/** RX characteristic UUID */
|
||||
private final static UUID UART_RX_CHARACTERISTIC_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
private static final UUID UART_RX_CHARACTERISTIC_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
/** TX characteristic UUID */
|
||||
private final static UUID UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
private static final UUID UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
/** The maximum packet size is 20 bytes. */
|
||||
private static final int MAX_PACKET_SIZE = 20;
|
||||
|
||||
@@ -64,7 +65,7 @@ public class UARTProfile extends BleProfile {
|
||||
private int mBufferOffset;
|
||||
|
||||
@Override
|
||||
protected Queue<BleManager.Request> initGatt(final BluetoothGatt gatt) {
|
||||
protected Deque<BleManager.Request> initGatt(final BluetoothGatt gatt) {
|
||||
final BluetoothGattService service = gatt.getService(UART_SERVICE_UUID);
|
||||
mTXCharacteristic = service.getCharacteristic(UART_TX_CHARACTERISTIC_UUID);
|
||||
mRXCharacteristic = service.getCharacteristic(UART_RX_CHARACTERISTIC_UUID);
|
||||
@@ -78,8 +79,8 @@ public class UARTProfile extends BleProfile {
|
||||
mRXCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
|
||||
|
||||
// We don't want to enable notifications on TX characteristic as we are not showing them here. A watch may be just used to send data. At least now.
|
||||
// final LinkedList<BleManager.Request> requests = new LinkedList<>();
|
||||
// requests.add(BleManager.Request.newEnableNotificationsRequest(mTXCharacteristic));
|
||||
// final LinkedList<BleProfileApi.Request> requests = new LinkedList<>();
|
||||
// requests.add(BleProfileApi.Request.newEnableNotificationsRequest(mTXCharacteristic));
|
||||
// return requests;
|
||||
return null;
|
||||
}
|
||||
@@ -106,11 +107,8 @@ public class UARTProfile extends BleProfile {
|
||||
mOutgoingBuffer = null;
|
||||
} else { // Otherwise...
|
||||
final int length = Math.min(buffer.length - mBufferOffset, MAX_PACKET_SIZE);
|
||||
final byte[] data = new byte[length]; // We send at most 20 bytes
|
||||
System.arraycopy(buffer, mBufferOffset, data, 0, length);
|
||||
getApi().enqueue(BleProfileApi.Request.newWriteRequest(mRXCharacteristic, buffer, mBufferOffset, length));
|
||||
mBufferOffset += length;
|
||||
mRXCharacteristic.setValue(data);
|
||||
getApi().writeCharacteristic(mRXCharacteristic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,15 +132,12 @@ public class UARTProfile extends BleProfile {
|
||||
|
||||
if (!writeRequest) { // no WRITE REQUEST property
|
||||
final int length = Math.min(buffer.length, MAX_PACKET_SIZE);
|
||||
final byte[] data = new byte[length]; // We send at most 20 bytes
|
||||
System.arraycopy(buffer, 0, data, 0, length);
|
||||
mBufferOffset += length;
|
||||
mRXCharacteristic.setValue(data);
|
||||
} else { // there is WRITE REQUEST property
|
||||
mRXCharacteristic.setValue(buffer);
|
||||
getApi().enqueue(BleProfileApi.Request.newWriteRequest(mRXCharacteristic, buffer, 0, length));
|
||||
} else { // there is WRITE REQUEST property, let's try Long Write
|
||||
mBufferOffset = buffer.length;
|
||||
getApi().enqueue(BleProfileApi.Request.newWriteRequest(mRXCharacteristic, buffer, 0, buffer.length));
|
||||
}
|
||||
getApi().writeCharacteristic(mRXCharacteristic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user