diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 795435df..aef5c37b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -33,6 +33,7 @@
+
getRecords() {
- return mManager.getRecords();
- }
+ public class CGMSBinder extends LocalBinder {
+ /**
+ * Returns all records as a sparse array where sequence number is the key.
+ *
+ * @return the records list
+ */
+ public SparseArray getRecords() {
+ return mManager.getRecords();
+ }
- /**
- * Clears the records list locally
- */
- public void clear() {
- if (mManager != null)
- mManager.clear();
- }
+ /**
+ * Clears the records list locally
+ */
+ public void clear() {
+ if (mManager != null)
+ mManager.clear();
+ }
- /**
- * Sends the request to obtain the first (oldest) record from glucose device.
- * The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access Control
- * Point indication with status code ({@link CGMSManager# RESPONSE_SUCCESS} or other in case of error.
- */
- public void getFirstRecord() {
- if (mManager != null)
- mManager.getFirstRecord();
- }
+ /**
+ * Sends the request to obtain the first (oldest) record from glucose device.
+ * The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access Control
+ * Point indication with status code ({@link CGMSManager# RESPONSE_SUCCESS} or other in case of error.
+ */
+ public void getFirstRecord() {
+ if (mManager != null)
+ mManager.getFirstRecord();
+ }
- /**
- * Sends the request to obtain the last (most recent) record from glucose device.
- * The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access
- * Control Point indication with status code Success or other in case of error.
- */
- public void getLastRecord() {
- if (mManager != null)
- mManager.getLastRecord();
- }
+ /**
+ * Sends the request to obtain the last (most recent) record from glucose device.
+ * The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access
+ * Control Point indication with status code Success or other in case of error.
+ */
+ public void getLastRecord() {
+ if (mManager != null)
+ mManager.getLastRecord();
+ }
- /**
- * Sends the request to obtain all records from glucose device.
- * Initially we want to notify user about the number of the records so the Report Number of Stored Records is send.
- * The data will be returned to Glucose Measurement characteristic as a series of notifications followed
- * by Record Access Control Point indication with status code Success or other in case of error.
- */
- public void getAllRecords() {
- if (mManager != null)
- mManager.getAllRecords();
- }
+ /**
+ * Sends the request to obtain all records from glucose device.
+ * Initially we want to notify user about the number of the records so the Report Number of Stored Records is send.
+ * The data will be returned to Glucose Measurement characteristic as a series of notifications followed
+ * by Record Access Control Point indication with status code Success or other in case of error.
+ */
+ public void getAllRecords() {
+ if (mManager != null)
+ mManager.getAllRecords();
+ }
- /**
- * Sends the request to obtain all records from glucose device with sequence number greater
- * than the last one already obtained. The data will be returned to Glucose Measurement
- * characteristic as a series of notifications followed by Record Access Control Point
- * indication with status code Success or other in case of error.
- */
- public void refreshRecords() {
- if (mManager != null)
- mManager.refreshRecords();
- }
+ /**
+ * Sends the request to obtain all records from glucose device with sequence number greater
+ * than the last one already obtained. The data will be returned to Glucose Measurement
+ * characteristic as a series of notifications followed by Record Access Control Point
+ * indication with status code Success or other in case of error.
+ */
+ public void refreshRecords() {
+ if (mManager != null)
+ mManager.refreshRecords();
+ }
- /**
- * Sends abort operation signal to the device
- */
- public void abort() {
- if (mManager != null)
- mManager.abort();
- }
+ /**
+ * Sends abort operation signal to the device
+ */
+ public void abort() {
+ if (mManager != null)
+ mManager.abort();
+ }
- /**
- * Sends Delete op code with All stored records parameter. This method may not be supported by the SDK sample.
- */
- public void deleteAllRecords() {
- if (mManager != null)
- mManager.deleteAllRecords();
- }
- }
+ /**
+ * Sends Delete op code with All stored records parameter. This method may not be supported by the SDK sample.
+ */
+ public void deleteAllRecords() {
+ if (mManager != null)
+ mManager.deleteAllRecords();
+ }
+ }
- @Override
- protected LocalBinder getBinder() {
- return mBinder;
- }
+ @Override
+ protected LocalBinder getBinder() {
+ return mBinder;
+ }
- @Override
- protected LoggableBleManager initializeManager() {
- return mManager = new CGMSManager(this);
- }
+ @Override
+ protected LoggableBleManager initializeManager() {
+ return mManager = new CGMSManager(this);
+ }
- @Override
- public void onCreate() {
- super.onCreate();
- final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_DISCONNECT);
- registerReceiver(mDisconnectActionBroadcastReceiver, filter);
- }
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_DISCONNECT);
+ registerReceiver(mDisconnectActionBroadcastReceiver, filter);
+ }
- @Override
- public void onDestroy() {
- // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
- cancelNotification();
- unregisterReceiver(mDisconnectActionBroadcastReceiver);
+ @Override
+ public void onDestroy() {
+ // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
+ stopForegroundService();
+ unregisterReceiver(mDisconnectActionBroadcastReceiver);
- super.onDestroy();
- }
+ super.onDestroy();
+ }
- @Override
- protected void onRebind() {
- // when the activity rebinds to the service, remove the notification
- cancelNotification();
- }
+ @Override
+ protected void onRebind() {
+ startForegroundService();
+ }
- @Override
- protected void onUnbind() {
- // when the activity closes we need to show the notification that user is connected to the sensor
- createNotification(R.string.csc_notification_connected_message, 0);
- }
+ @Override
+ protected void onUnbind() {
+ startForegroundService();
+ }
- /**
- * Creates the notification
- *
- * @param messageResId the message resource id. The message must have one String parameter,
- * f.e. <string name="name">%s is connected</string>
- * @param defaults signals that will be used to notify the user
- */
- private void createNotification(final int messageResId, final int defaults) {
- final Intent parentIntent = new Intent(this, FeaturesActivity.class);
- parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final Intent targetIntent = new Intent(this, CGMSActivity.class);
+ /**
+ * Sets the service as a foreground service
+ */
+ private void startForegroundService(){
+ // when the activity closes we need to show the notification that user is connected to the peripheral sensor
+ // We start the service as a foreground service as Android 8.0 (Oreo) onwards kills any running background services
+ final Notification notification = createNotification(R.string.uart_notification_connected_message, 0);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForeground(NOTIFICATION_ID, notification);
+ } else {
+ final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(NOTIFICATION_ID, notification);
+ }
+ }
- final Intent disconnect = new Intent(ACTION_DISCONNECT);
- final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
+ /**
+ * Stops the service as a foreground service
+ */
+ private void stopForegroundService(){
+ // when the activity rebinds to the service, remove the notification and stop the foreground service
+ // on devices running Android 8.0 (Oreo) or above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ stopForeground(true);
+ } else {
+ cancelNotification();
+ }
+ }
- // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
- final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[]{parentIntent, targetIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
- builder.setContentIntent(pendingIntent);
- builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
- builder.setSmallIcon(R.drawable.ic_stat_notify_cgms);
- builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
- builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.csc_notification_action_disconnect), disconnectAction));
+ /**
+ * Creates the notification
+ *
+ * @param messageResId the message resource id. The message must have one String parameter,
+ * f.e. <string name="name">%s is connected</string>
+ * @param defaults signals that will be used to notify the user
+ */
+ private Notification createNotification(final int messageResId, final int defaults) {
+ final Intent parentIntent = new Intent(this, FeaturesActivity.class);
+ parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Intent targetIntent = new Intent(this, CGMSActivity.class);
- final Notification notification = builder.build();
- final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(NOTIFICATION_ID, notification);
- }
+ final Intent disconnect = new Intent(ACTION_DISCONNECT);
+ final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
- /**
- * Cancels the existing notification. If there is no active notification this method does nothing
- */
- private void cancelNotification() {
- final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- nm.cancel(NOTIFICATION_ID);
- }
+ // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
+ final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[]{parentIntent, targetIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
+ builder.setContentIntent(pendingIntent);
+ builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
+ builder.setSmallIcon(R.drawable.ic_stat_notify_cgms);
+ builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
+ builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.csc_notification_action_disconnect), disconnectAction));
- /**
- * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
- */
- private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
- if (isConnected())
- getBinder().disconnect();
- else
- stopSelf();
- }
- };
+ return builder.build();
+ }
- @Override
- public void onCGMValueReceived(@NonNull final BluetoothDevice device, final CGMSRecord record) {
- final Intent broadcast = new Intent(BROADCAST_NEW_CGMS_VALUE);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_CGMS_RECORD, record);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ /**
+ * Cancels the existing notification. If there is no active notification this method does nothing
+ */
+ private void cancelNotification() {
+ final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ nm.cancel(NOTIFICATION_ID);
+ }
- @Override
- public void onOperationStarted(@NonNull final BluetoothDevice device) {
- final Intent broadcast = new Intent(OPERATION_STARTED);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_DATA, true);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ /**
+ * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
+ */
+ private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
+ if (isConnected())
+ getBinder().disconnect();
+ else
+ stopSelf();
+ }
+ };
- @Override
- public void onOperationCompleted(@NonNull final BluetoothDevice device) {
- final Intent broadcast = new Intent(OPERATION_COMPLETED);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_DATA, true);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onCGMValueReceived(@NonNull final BluetoothDevice device, final CGMSRecord record) {
+ final Intent broadcast = new Intent(BROADCAST_NEW_CGMS_VALUE);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_CGMS_RECORD, record);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onOperationFailed(@NonNull final BluetoothDevice device) {
- final Intent broadcast = new Intent(OPERATION_FAILED);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_DATA, true);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onOperationStarted(@NonNull final BluetoothDevice device) {
+ final Intent broadcast = new Intent(OPERATION_STARTED);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_DATA, true);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onOperationAborted(@NonNull final BluetoothDevice device) {
- final Intent broadcast = new Intent(OPERATION_ABORTED);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_DATA, true);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onOperationCompleted(@NonNull final BluetoothDevice device) {
+ final Intent broadcast = new Intent(OPERATION_COMPLETED);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_DATA, true);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onOperationNotSupported(@NonNull final BluetoothDevice device) {
- final Intent broadcast = new Intent(OPERATION_NOT_SUPPORTED);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_DATA, false);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onOperationFailed(@NonNull final BluetoothDevice device) {
+ final Intent broadcast = new Intent(OPERATION_FAILED);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_DATA, true);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onDatasetCleared(@NonNull final BluetoothDevice device) {
- final Intent broadcast = new Intent(BROADCAST_DATA_SET_CLEAR);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onOperationAborted(@NonNull final BluetoothDevice device) {
+ final Intent broadcast = new Intent(OPERATION_ABORTED);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_DATA, true);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onNumberOfRecordsRequested(@NonNull final BluetoothDevice device, final int value) {
- if (value == 0)
- showToast(R.string.gls_progress_zero);
- else
- showToast(getResources().getQuantityString(R.plurals.gls_progress, value, value));
+ @Override
+ public void onOperationNotSupported(@NonNull final BluetoothDevice device) {
+ final Intent broadcast = new Intent(OPERATION_NOT_SUPPORTED);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_DATA, false);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- }
+ @Override
+ public void onDatasetCleared(@NonNull final BluetoothDevice device) {
+ final Intent broadcast = new Intent(BROADCAST_DATA_SET_CLEAR);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
- final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
- broadcast.putExtra(EXTRA_DEVICE, device);
- broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onNumberOfRecordsRequested(@NonNull final BluetoothDevice device, final int value) {
+ if (value == 0)
+ showToast(R.string.gls_progress_zero);
+ else
+ showToast(getResources().getQuantityString(R.plurals.gls_progress, value, value));
+
+ }
+
+ @Override
+ public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
+ final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
+ broadcast.putExtra(EXTRA_DEVICE, device);
+ broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
}
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java
index 2a99f0c3..e29bc71d 100644
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java
@@ -30,10 +30,11 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Build;
+
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-
import no.nordicsemi.android.log.Logger;
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
import no.nordicsemi.android.nrftoolbox.R;
@@ -42,168 +43,195 @@ import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
import no.nordicsemi.android.nrftoolbox.profile.LoggableBleManager;
public class CSCService extends BleProfileService implements CSCManagerCallbacks {
- @SuppressWarnings("unused")
- private static final String TAG = "CSCService";
+ @SuppressWarnings("unused")
+ private static final String TAG = "CSCService";
- public static final String BROADCAST_WHEEL_DATA = "no.nordicsemi.android.nrftoolbox.csc.BROADCAST_WHEEL_DATA";
- /** Speed in meters per second. */
- public static final String EXTRA_SPEED = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_SPEED";
- /** Distance in meters. */
- public static final String EXTRA_DISTANCE = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_DISTANCE";
- /** Total distance in meters. */
- public static final String EXTRA_TOTAL_DISTANCE = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_TOTAL_DISTANCE";
+ public static final String BROADCAST_WHEEL_DATA = "no.nordicsemi.android.nrftoolbox.csc.BROADCAST_WHEEL_DATA";
+ /**
+ * Speed in meters per second.
+ */
+ public static final String EXTRA_SPEED = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_SPEED";
+ /**
+ * Distance in meters.
+ */
+ public static final String EXTRA_DISTANCE = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_DISTANCE";
+ /**
+ * Total distance in meters.
+ */
+ public static final String EXTRA_TOTAL_DISTANCE = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_TOTAL_DISTANCE";
- public static final String BROADCAST_CRANK_DATA = "no.nordicsemi.android.nrftoolbox.csc.BROADCAST_CRANK_DATA";
- public static final String EXTRA_GEAR_RATIO = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_GEAR_RATIO";
- public static final String EXTRA_CADENCE = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_CADENCE";
+ public static final String BROADCAST_CRANK_DATA = "no.nordicsemi.android.nrftoolbox.csc.BROADCAST_CRANK_DATA";
+ public static final String EXTRA_GEAR_RATIO = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_GEAR_RATIO";
+ public static final String EXTRA_CADENCE = "no.nordicsemi.android.nrftoolbox.csc.EXTRA_CADENCE";
- public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
- public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
+ public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
+ public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
- private static final String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.csc.ACTION_DISCONNECT";
+ private static final String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.csc.ACTION_DISCONNECT";
- private final static int NOTIFICATION_ID = 200;
- private final static int OPEN_ACTIVITY_REQ = 0;
- private final static int DISCONNECT_REQ = 1;
+ private final static int NOTIFICATION_ID = 200;
+ private final static int OPEN_ACTIVITY_REQ = 0;
+ private final static int DISCONNECT_REQ = 1;
- private final LocalBinder mBinder = new CSCBinder();
- private CSCManager mManager;
+ private final LocalBinder mBinder = new CSCBinder();
+ private CSCManager mManager;
- /**
- * This local binder is an interface for the bonded activity to operate with the RSC sensor
- */
- class CSCBinder extends LocalBinder {
- // empty
- }
+ /**
+ * This local binder is an interface for the bonded activity to operate with the RSC sensor
+ */
+ class CSCBinder extends LocalBinder {
+ // empty
+ }
- @Override
- protected LocalBinder getBinder() {
- return mBinder;
- }
+ @Override
+ protected LocalBinder getBinder() {
+ return mBinder;
+ }
- @Override
- protected LoggableBleManager initializeManager() {
- return mManager = new CSCManager(this);
- }
+ @Override
+ protected LoggableBleManager initializeManager() {
+ return mManager = new CSCManager(this);
+ }
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_DISCONNECT);
- registerReceiver(mDisconnectActionBroadcastReceiver, filter);
- }
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_DISCONNECT);
+ registerReceiver(mDisconnectActionBroadcastReceiver, filter);
+ }
- @Override
- public void onDestroy() {
- // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
- cancelNotification();
- unregisterReceiver(mDisconnectActionBroadcastReceiver);
+ @Override
+ public void onDestroy() {
+ // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
+ cancelNotification();
+ unregisterReceiver(mDisconnectActionBroadcastReceiver);
- super.onDestroy();
- }
+ super.onDestroy();
+ }
- @Override
- protected void onRebind() {
- // when the activity rebinds to the service, remove the notification
- cancelNotification();
+ @Override
+ protected void onRebind() {
+ stopForegroundService();
- if (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.
- mManager.readBatteryLevelCharacteristic();
- }
- }
+ if (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.
+ mManager.readBatteryLevelCharacteristic();
+ }
+ }
- @Override
- protected void 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 (isConnected())
- mManager.disableBatteryLevelCharacteristicNotifications();
+ @Override
+ protected void 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 (isConnected())
+ mManager.disableBatteryLevelCharacteristicNotifications();
+ startForegroundService();
+ }
- // when the activity closes we need to show the notification that user is connected to the sensor
- createNotification(R.string.csc_notification_connected_message, 0);
- }
+ @Override
+ public void onDistanceChanged(@NonNull final BluetoothDevice device, final float totalDistance, final float distance, final float speed) {
+ final Intent broadcast = new Intent(BROADCAST_WHEEL_DATA);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_SPEED, speed);
+ broadcast.putExtra(EXTRA_DISTANCE, distance);
+ broadcast.putExtra(EXTRA_TOTAL_DISTANCE, totalDistance);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onDistanceChanged(@NonNull final BluetoothDevice device, final float totalDistance, final float distance, final float speed) {
- final Intent broadcast = new Intent(BROADCAST_WHEEL_DATA);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_SPEED, speed);
- broadcast.putExtra(EXTRA_DISTANCE, distance);
- broadcast.putExtra(EXTRA_TOTAL_DISTANCE, totalDistance);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onCrankDataChanged(@NonNull final BluetoothDevice device, final float crankCadence, final float gearRatio) {
+ final Intent broadcast = new Intent(BROADCAST_CRANK_DATA);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_GEAR_RATIO, gearRatio);
+ broadcast.putExtra(EXTRA_CADENCE, (int) crankCadence);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onCrankDataChanged(@NonNull final BluetoothDevice device, final float crankCadence, final float gearRatio) {
- final Intent broadcast = new Intent(BROADCAST_CRANK_DATA);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_GEAR_RATIO, gearRatio);
- broadcast.putExtra(EXTRA_CADENCE, (int) crankCadence);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
+ final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- @Override
- public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
- final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ /**
+ * Sets the service as a foreground service
+ */
+ private void startForegroundService(){
+ // when the activity closes we need to show the notification that user is connected to the peripheral sensor
+ // We start the service as a foreground service as Android 8.0 (Oreo) onwards kills any running background services
+ final Notification notification = createNotification(R.string.uart_notification_connected_message, 0);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForeground(NOTIFICATION_ID, notification);
+ } else {
+ final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(NOTIFICATION_ID, notification);
+ }
+ }
- /**
- * Creates the notification
- *
- * @param messageResId
- * the message resource id. The message must have one String parameter,
- * f.e. <string name="name">%s is connected</string>
- * @param defaults
- * signals that will be used to notify the user
- */
- private void createNotification(final int messageResId, final int defaults) {
- final Intent parentIntent = new Intent(this, FeaturesActivity.class);
- parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final Intent targetIntent = new Intent(this, CSCActivity.class);
+ /**
+ * Stops the service as a foreground service
+ */
+ private void stopForegroundService(){
+ // when the activity rebinds to the service, remove the notification and stop the foreground service
+ // on devices running Android 8.0 (Oreo) or above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ stopForeground(true);
+ } else {
+ cancelNotification();
+ }
+ }
- final Intent disconnect = new Intent(ACTION_DISCONNECT);
- final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
+ /**
+ * Creates the notification
+ *
+ * @param messageResId the message resource id. The message must have one String parameter,
+ * f.e. <string name="name">%s is connected</string>
+ * @param defaults
+ */
+ private Notification createNotification(final int messageResId, final int defaults) {
+ final Intent parentIntent = new Intent(this, FeaturesActivity.class);
+ parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Intent targetIntent = new Intent(this, CSCActivity.class);
- // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
- final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[] { parentIntent, targetIntent }, PendingIntent.FLAG_UPDATE_CURRENT);
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
- builder.setContentIntent(pendingIntent);
- builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
- builder.setSmallIcon(R.drawable.ic_stat_notify_csc);
- builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
- builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.csc_notification_action_disconnect), disconnectAction));
+ final Intent disconnect = new Intent(ACTION_DISCONNECT);
+ final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
- final Notification notification = builder.build();
- final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(NOTIFICATION_ID, notification);
- }
+ // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
+ final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[]{parentIntent, targetIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
+ builder.setContentIntent(pendingIntent);
+ builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
+ builder.setSmallIcon(R.drawable.ic_stat_notify_csc);
+ builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
+ builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.csc_notification_action_disconnect), disconnectAction));
- /**
- * Cancels the existing notification. If there is no active notification this method does nothing
- */
- private void cancelNotification() {
- final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- nm.cancel(NOTIFICATION_ID);
- }
+ return builder.build();
+ }
- /**
- * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
- */
- private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
- if (isConnected())
- getBinder().disconnect();
- else
- stopSelf();
- }
- };
+ /**
+ * Cancels the existing notification. If there is no active notification this method does nothing
+ */
+ private void cancelNotification() {
+ final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ nm.cancel(NOTIFICATION_ID);
+ }
+
+ /**
+ * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
+ */
+ private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
+ if (isConnected())
+ getBinder().disconnect();
+ else
+ stopSelf();
+ }
+ };
}
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSService.java
index 811740ea..2f9ed139 100644
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSService.java
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSService.java
@@ -30,6 +30,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Build;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
@@ -110,14 +112,12 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks
@Override
protected void onRebind() {
- // when the activity rebinds to the service, remove the notification
- cancelNotification();
+ stopForegroundService();
}
@Override
protected void onUnbind() {
- // when the activity closes we need to show the notification that user is connected to the sensor
- createNotification(R.string.hts_notification_connected_message, 0);
+ startForegroundService();
}
@Override
@@ -153,16 +153,42 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
+ /**
+ * Sets the service as a foreground service
+ */
+ private void startForegroundService(){
+ // when the activity closes we need to show the notification that user is connected to the peripheral sensor
+ // We start the service as a foreground service as Android 8.0 (Oreo) onwards kills any running background services
+ final Notification notification = createNotification(R.string.uart_notification_connected_message, 0);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForeground(NOTIFICATION_ID, notification);
+ } else {
+ final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(NOTIFICATION_ID, notification);
+ }
+ }
+
+ /**
+ * Stops the service as a foreground service
+ */
+ private void stopForegroundService(){
+ // when the activity rebinds to the service, remove the notification and stop the foreground service
+ // on devices running Android 8.0 (Oreo) or above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ stopForeground(true);
+ } else {
+ cancelNotification();
+ }
+ }
+
/**
* Creates the notification
- *
- * @param messageResId
+ * @param messageResId
* message resource id. The message must have one String parameter,
* f.e. <string name="name">%s is connected</string>
* @param defaults
- * signals that will be used to notify the user
*/
- private void createNotification(final int messageResId, final int defaults) {
+ private Notification createNotification(final int messageResId, final int defaults) {
final Intent parentIntent = new Intent(this, FeaturesActivity.class);
parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final Intent targetIntent = new Intent(this, HTSActivity.class);
@@ -179,9 +205,7 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks
builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.hts_notification_action_disconnect), disconnectAction));
- final Notification notification = builder.build();
- final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(NOTIFICATION_ID, notification);
+ return builder.build();
}
/**
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCService.java
index 04480330..a7b6f369 100644
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCService.java
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCService.java
@@ -30,12 +30,13 @@ 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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-
import no.nordicsemi.android.log.Logger;
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
import no.nordicsemi.android.nrftoolbox.R;
@@ -44,209 +45,239 @@ import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
import no.nordicsemi.android.nrftoolbox.profile.LoggableBleManager;
public class RSCService extends BleProfileService implements RSCManagerCallbacks {
- private static final String TAG = "RSCService";
+ private static final String TAG = "RSCService";
- public static final String BROADCAST_RSC_MEASUREMENT = "no.nordicsemi.android.nrftoolbox.rsc.BROADCAST_RSC_MEASUREMENT";
- public static final String EXTRA_SPEED = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_SPEED";
- public static final String EXTRA_CADENCE = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_CADENCE";
- public static final String EXTRA_STRIDE_LENGTH = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_STRIDE_LENGTH";
- public static final String EXTRA_TOTAL_DISTANCE = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_TOTAL_DISTANCE";
- public static final String EXTRA_ACTIVITY = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_ACTIVITY";
+ public static final String BROADCAST_RSC_MEASUREMENT = "no.nordicsemi.android.nrftoolbox.rsc.BROADCAST_RSC_MEASUREMENT";
+ public static final String EXTRA_SPEED = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_SPEED";
+ public static final String EXTRA_CADENCE = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_CADENCE";
+ public static final String EXTRA_STRIDE_LENGTH = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_STRIDE_LENGTH";
+ public static final String EXTRA_TOTAL_DISTANCE = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_TOTAL_DISTANCE";
+ public static final String EXTRA_ACTIVITY = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_ACTIVITY";
- public static final String BROADCAST_STRIDES_UPDATE = "no.nordicsemi.android.nrftoolbox.rsc.BROADCAST_STRIDES_UPDATE";
- public static final String EXTRA_STRIDES = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_STRIDES";
- public static final String EXTRA_DISTANCE = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_DISTANCE";
+ public static final String BROADCAST_STRIDES_UPDATE = "no.nordicsemi.android.nrftoolbox.rsc.BROADCAST_STRIDES_UPDATE";
+ public static final String EXTRA_STRIDES = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_STRIDES";
+ public static final String EXTRA_DISTANCE = "no.nordicsemi.android.nrftoolbox.rsc.EXTRA_DISTANCE";
- public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
- public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
+ public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
+ public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
- private final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.rsc.ACTION_DISCONNECT";
+ private final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.rsc.ACTION_DISCONNECT";
- private RSCManager mManager;
+ private RSCManager mManager;
- /** The last value of a cadence */
- private float mCadence;
- /** Trip distance in cm */
- private long mDistance;
- /** Stride length in cm */
- private Integer mStrideLength;
- /** Number of steps in the trip */
- private int mStepsNumber;
- private boolean mTaskInProgress;
- private final Handler mHandler = new Handler();
+ /**
+ * The last value of a cadence
+ */
+ private float mCadence;
+ /**
+ * Trip distance in cm
+ */
+ private long mDistance;
+ /**
+ * Stride length in cm
+ */
+ private Integer mStrideLength;
+ /**
+ * Number of steps in the trip
+ */
+ private int mStepsNumber;
+ private boolean mTaskInProgress;
+ private final Handler mHandler = new Handler();
- private final static int NOTIFICATION_ID = 200;
- private final static int OPEN_ACTIVITY_REQ = 0;
- private final static int DISCONNECT_REQ = 1;
+ private final static int NOTIFICATION_ID = 200;
+ private final static int OPEN_ACTIVITY_REQ = 0;
+ private final static int DISCONNECT_REQ = 1;
- private final LocalBinder mBinder = new RSCBinder();
+ private final LocalBinder mBinder = new RSCBinder();
- /**
- * This local binder is an interface for the bound activity to operate with the RSC sensor.
- */
- class RSCBinder extends LocalBinder {
- // empty
- }
+ /**
+ * This local binder is an interface for the bound activity to operate with the RSC sensor.
+ */
+ class RSCBinder extends LocalBinder {
+ // empty
+ }
- @Override
- protected LocalBinder getBinder() {
- return mBinder;
- }
+ @Override
+ protected LocalBinder getBinder() {
+ return mBinder;
+ }
- @Override
- protected LoggableBleManager initializeManager() {
- return mManager = new RSCManager(this);
- }
+ @Override
+ protected LoggableBleManager initializeManager() {
+ return mManager = new RSCManager(this);
+ }
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_DISCONNECT);
- registerReceiver(mDisconnectActionBroadcastReceiver, filter);
- }
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_DISCONNECT);
+ registerReceiver(mDisconnectActionBroadcastReceiver, filter);
+ }
- @Override
- public void onDestroy() {
- // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
- cancelNotification();
- unregisterReceiver(mDisconnectActionBroadcastReceiver);
+ @Override
+ public void onDestroy() {
+ // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
+ stopForegroundService();
+ unregisterReceiver(mDisconnectActionBroadcastReceiver);
- super.onDestroy();
- }
+ super.onDestroy();
+ }
- @Override
- protected void onRebind() {
- // when the activity rebinds to the service, remove the notification
- cancelNotification();
+ @Override
+ protected void onRebind() {
+ stopForegroundService();
- if (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.
- mManager.readBatteryLevelCharacteristic();
- }
- }
+ if (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.
+ mManager.readBatteryLevelCharacteristic();
+ }
+ }
- @Override
- protected void 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 (isConnected())
- mManager.disableBatteryLevelCharacteristicNotifications();
+ @Override
+ protected void 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 (isConnected())
+ mManager.disableBatteryLevelCharacteristicNotifications();
- // when the activity closes we need to show the notification that user is connected to the sensor
- createNotification(R.string.rsc_notification_connected_message, 0);
- }
+ startForegroundService();
+ }
- private final Runnable mUpdateStridesTask = new Runnable() {
- @Override
- public void run() {
- if (!isConnected())
- return;
+ private final Runnable mUpdateStridesTask = new Runnable() {
+ @Override
+ public void run() {
+ if (!isConnected())
+ return;
- mStepsNumber++;
- mDistance += mStrideLength; // [cm]
- final Intent broadcast = new Intent(BROADCAST_STRIDES_UPDATE);
- broadcast.putExtra(EXTRA_STRIDES, mStepsNumber);
- broadcast.putExtra(EXTRA_DISTANCE, mDistance);
- LocalBroadcastManager.getInstance(RSCService.this).sendBroadcast(broadcast);
+ mStepsNumber++;
+ mDistance += mStrideLength; // [cm]
+ final Intent broadcast = new Intent(BROADCAST_STRIDES_UPDATE);
+ broadcast.putExtra(EXTRA_STRIDES, mStepsNumber);
+ broadcast.putExtra(EXTRA_DISTANCE, mDistance);
+ LocalBroadcastManager.getInstance(RSCService.this).sendBroadcast(broadcast);
- if (mCadence > 0) {
- final long interval = (long) (1000.0f * 60.0f / mCadence);
- mHandler.postDelayed(mUpdateStridesTask, interval);
- } else {
- mTaskInProgress = false;
- }
- }
- };
+ if (mCadence > 0) {
+ final long interval = (long) (1000.0f * 60.0f / mCadence);
+ mHandler.postDelayed(mUpdateStridesTask, interval);
+ } else {
+ mTaskInProgress = false;
+ }
+ }
+ };
- @Override
- public void onRSCMeasurementReceived(@NonNull final BluetoothDevice device, final boolean running,
- final float instantaneousSpeed, final int instantaneousCadence,
- @Nullable final Integer strideLength,
- @Nullable final Long totalDistance) {
- final Intent broadcast = new Intent(BROADCAST_RSC_MEASUREMENT);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_SPEED, instantaneousSpeed);
- broadcast.putExtra(EXTRA_CADENCE, instantaneousCadence);
- broadcast.putExtra(EXTRA_STRIDE_LENGTH, strideLength);
- broadcast.putExtra(EXTRA_TOTAL_DISTANCE, totalDistance);
- broadcast.putExtra(EXTRA_ACTIVITY, running);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ @Override
+ public void onRSCMeasurementReceived(@NonNull final BluetoothDevice device, final boolean running,
+ final float instantaneousSpeed, final int instantaneousCadence,
+ @Nullable final Integer strideLength,
+ @Nullable final Long totalDistance) {
+ final Intent broadcast = new Intent(BROADCAST_RSC_MEASUREMENT);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_SPEED, instantaneousSpeed);
+ broadcast.putExtra(EXTRA_CADENCE, instantaneousCadence);
+ broadcast.putExtra(EXTRA_STRIDE_LENGTH, strideLength);
+ broadcast.putExtra(EXTRA_TOTAL_DISTANCE, totalDistance);
+ broadcast.putExtra(EXTRA_ACTIVITY, running);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- // Start strides counter if not in progress
- mCadence = instantaneousCadence;
- if (strideLength != null) {
- mStrideLength = strideLength;
- }
- if (!mTaskInProgress && strideLength != null && instantaneousCadence > 0) {
- mTaskInProgress = true;
+ // Start strides counter if not in progress
+ mCadence = instantaneousCadence;
+ if (strideLength != null) {
+ mStrideLength = strideLength;
+ }
+ if (!mTaskInProgress && strideLength != null && instantaneousCadence > 0) {
+ mTaskInProgress = true;
- final long interval = (long) (1000.0f * 60.0f / mCadence);
- mHandler.postDelayed(mUpdateStridesTask, interval);
- }
- }
+ final long interval = (long) (1000.0f * 60.0f / mCadence);
+ mHandler.postDelayed(mUpdateStridesTask, interval);
+ }
+ }
- @Override
- public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int value) {
- final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_BATTERY_LEVEL, value);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int value) {
+ final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_BATTERY_LEVEL, value);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- /**
- * Creates the notification
- *
- * @param messageResId
- * message resource id. The message must have one String parameter,
- * f.e. <string name="name">%s is connected</string>
- * @param defaults
- * signals that will be used to notify the user
- */
- private void createNotification(final int messageResId, final int defaults) {
- final Intent parentIntent = new Intent(this, FeaturesActivity.class);
- parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final Intent targetIntent = new Intent(this, RSCActivity.class);
+ /**
+ * Sets the service as a foreground service
+ */
+ private void startForegroundService(){
+ // when the activity closes we need to show the notification that user is connected to the peripheral sensor
+ // We start the service as a foreground service as Android 8.0 (Oreo) onwards kills any running background services
+ final Notification notification = createNotification(R.string.uart_notification_connected_message, 0);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForeground(NOTIFICATION_ID, notification);
+ } else {
+ final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(NOTIFICATION_ID, notification);
+ }
+ }
- final Intent disconnect = new Intent(ACTION_DISCONNECT);
- final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
+ /**
+ * Stops the service as a foreground service
+ */
+ private void stopForegroundService(){
+ // when the activity rebinds to the service, remove the notification and stop the foreground service
+ // on devices running Android 8.0 (Oreo) or above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ stopForeground(true);
+ } else {
+ cancelNotification();
+ }
+ }
- // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
- final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[] { parentIntent, targetIntent }, PendingIntent.FLAG_UPDATE_CURRENT);
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
- builder.setContentIntent(pendingIntent);
- builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
- builder.setSmallIcon(R.drawable.ic_stat_notify_rsc);
- builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
- builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.rsc_notification_action_disconnect), disconnectAction));
+ /**
+ * Creates the notification
+ *
+ * @param messageResId message resource id. The message must have one String parameter,
+ * f.e. <string name="name">%s is connected</string>
+ * @param defaults
+ */
+ private Notification createNotification(final int messageResId, final int defaults) {
+ final Intent parentIntent = new Intent(this, FeaturesActivity.class);
+ parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Intent targetIntent = new Intent(this, RSCActivity.class);
- final Notification notification = builder.build();
- final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(NOTIFICATION_ID, notification);
- }
+ final Intent disconnect = new Intent(ACTION_DISCONNECT);
+ final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
- /**
- * Cancels the existing notification. If there is no active notification this method does nothing
- */
- private void cancelNotification() {
- final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- nm.cancel(NOTIFICATION_ID);
- }
+ // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
+ final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[]{parentIntent, targetIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
+ builder.setContentIntent(pendingIntent);
+ builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
+ builder.setSmallIcon(R.drawable.ic_stat_notify_rsc);
+ builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
+ builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.rsc_notification_action_disconnect), disconnectAction));
- /**
- * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
- */
- private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
- if (isConnected())
- getBinder().disconnect();
- else
- stopSelf();
- }
- };
+ return builder.build();
+ }
+
+ /**
+ * Cancels the existing notification. If there is no active notification this method does nothing
+ */
+ private void cancelNotification() {
+ final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ nm.cancel(NOTIFICATION_ID);
+ }
+
+ /**
+ * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
+ */
+ private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
+ if (isConnected())
+ getBinder().disconnect();
+ else
+ stopSelf();
+ }
+ };
}
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java
index e6be074e..069d93c9 100644
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java
@@ -30,10 +30,11 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Build;
+
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-
import no.nordicsemi.android.log.Logger;
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
import no.nordicsemi.android.nrftoolbox.R;
@@ -42,144 +43,168 @@ import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
import no.nordicsemi.android.nrftoolbox.profile.LoggableBleManager;
public class TemplateService extends BleProfileService implements TemplateManagerCallbacks {
- public static final String BROADCAST_TEMPLATE_MEASUREMENT = "no.nordicsemi.android.nrftoolbox.template.BROADCAST_MEASUREMENT";
- public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.template.EXTRA_DATA";
+ public static final String BROADCAST_TEMPLATE_MEASUREMENT = "no.nordicsemi.android.nrftoolbox.template.BROADCAST_MEASUREMENT";
+ public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.template.EXTRA_DATA";
- public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
- public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
+ public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
+ public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
- private final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.template.ACTION_DISCONNECT";
+ private final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.template.ACTION_DISCONNECT";
- private final static int NOTIFICATION_ID = 864;
- private final static int OPEN_ACTIVITY_REQ = 0;
- private final static int DISCONNECT_REQ = 1;
+ private final static int NOTIFICATION_ID = 864;
+ private final static int OPEN_ACTIVITY_REQ = 0;
+ private final static int DISCONNECT_REQ = 1;
- private TemplateManager mManager;
+ private TemplateManager mManager;
- private final LocalBinder mBinder = new TemplateBinder();
+ private final LocalBinder mBinder = new TemplateBinder();
- /**
- * This local binder is an interface for the bound activity to operate with the sensor.
- */
- class TemplateBinder extends LocalBinder {
- // TODO Define service API that may be used by a bound Activity
+ /**
+ * This local binder is an interface for the bound activity to operate with the sensor.
+ */
+ class TemplateBinder extends LocalBinder {
+ // TODO Define service API that may be used by a bound Activity
- /**
- * Sends some important data to the device.
- *
- * @param parameter some parameter.
- */
- public void performAction(final String parameter) {
- mManager.performAction(parameter);
- }
- }
+ /**
+ * Sends some important data to the device.
+ *
+ * @param parameter some parameter.
+ */
+ public void performAction(final String parameter) {
+ mManager.performAction(parameter);
+ }
+ }
- @Override
- protected LocalBinder getBinder() {
- return mBinder;
- }
+ @Override
+ protected LocalBinder getBinder() {
+ return mBinder;
+ }
- @Override
- protected LoggableBleManager initializeManager() {
- return mManager = new TemplateManager(this);
- }
+ @Override
+ protected LoggableBleManager initializeManager() {
+ return mManager = new TemplateManager(this);
+ }
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_DISCONNECT);
- registerReceiver(mDisconnectActionBroadcastReceiver, filter);
- }
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_DISCONNECT);
+ registerReceiver(mDisconnectActionBroadcastReceiver, filter);
+ }
- @Override
- public void onDestroy() {
- // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
- cancelNotification();
- unregisterReceiver(mDisconnectActionBroadcastReceiver);
+ @Override
+ public void onDestroy() {
+ // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
+ stopForegroundService();
+ unregisterReceiver(mDisconnectActionBroadcastReceiver);
- super.onDestroy();
- }
+ super.onDestroy();
+ }
- @Override
- protected void onRebind() {
- // when the activity rebinds to the service, remove the notification
- cancelNotification();
- }
+ @Override
+ protected void onRebind() {
+ stopForegroundService();
+ }
- @Override
- protected void onUnbind() {
- // when the activity closes we need to show the notification that user is connected to the sensor
- createNotification(R.string.template_notification_connected_message, 0);
- }
+ @Override
+ protected void onUnbind() {
+ startForegroundService();
+ }
- @Override
- public void onSampleValueReceived(@NonNull final BluetoothDevice device, final int value) {
- final Intent broadcast = new Intent(BROADCAST_TEMPLATE_MEASUREMENT);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_DATA, value);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ @Override
+ public void onSampleValueReceived(@NonNull final BluetoothDevice device, final int value) {
+ final Intent broadcast = new Intent(BROADCAST_TEMPLATE_MEASUREMENT);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_DATA, value);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- if (!mBound) {
- // Here we may update the notification to display the current value.
- // TODO modify the notification here
- }
- }
+ if (!mBound) {
+ // Here we may update the notification to display the current value.
+ // TODO modify the notification here
+ }
+ }
- @Override
- public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
+ @Override
+ public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
- }
+ }
- /**
- * Creates the notification.
- *
- * @param messageResId message resource id. The message must have one String parameter,
- * f.e. <string name="name">%s is connected</string>
- * @param defaults signals that will be used to notify the user
- */
- private void createNotification(final int messageResId, final int defaults) {
- final Intent parentIntent = new Intent(this, FeaturesActivity.class);
- parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final Intent targetIntent = new Intent(this, TemplateActivity.class);
+ /**
+ * Sets the service as a foreground service
+ */
+ private void startForegroundService(){
+ // when the activity closes we need to show the notification that user is connected to the peripheral sensor
+ // We start the service as a foreground service as Android 8.0 (Oreo) onwards kills any running background services
+ final Notification notification = createNotification(R.string.uart_notification_connected_message, 0);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForeground(NOTIFICATION_ID, notification);
+ } else {
+ final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(NOTIFICATION_ID, notification);
+ }
+ }
- final Intent disconnect = new Intent(ACTION_DISCONNECT);
- final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
+ /**
+ * Stops the service as a foreground service
+ */
+ private void stopForegroundService(){
+ // when the activity rebinds to the service, remove the notification and stop the foreground service
+ // on devices running Android 8.0 (Oreo) or above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ stopForeground(true);
+ } else {
+ cancelNotification();
+ }
+ }
- // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
- final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[]{parentIntent, targetIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
- builder.setContentIntent(pendingIntent);
- builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
- builder.setSmallIcon(R.drawable.ic_stat_notify_template);
- builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
- builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.template_notification_action_disconnect), disconnectAction));
+ /**
+ * Creates the notification.
+ *
+ * @param messageResId message resource id. The message must have one String parameter,
+ * f.e. <string name="name">%s is connected</string>
+ * @param defaults signals that will be used to notify the user
+ */
+ private Notification createNotification(final int messageResId, final int defaults) {
+ final Intent parentIntent = new Intent(this, FeaturesActivity.class);
+ parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Intent targetIntent = new Intent(this, TemplateActivity.class);
- final Notification notification = builder.build();
- final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(NOTIFICATION_ID, notification);
- }
+ final Intent disconnect = new Intent(ACTION_DISCONNECT);
+ final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
- /**
- * Cancels the existing notification. If there is no active notification this method does nothing
- */
- private void cancelNotification() {
- final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- nm.cancel(NOTIFICATION_ID);
- }
+ // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
+ final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[]{parentIntent, targetIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
+ builder.setContentIntent(pendingIntent);
+ builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
+ builder.setSmallIcon(R.drawable.ic_stat_notify_template);
+ builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
+ builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.template_notification_action_disconnect), disconnectAction));
- /**
- * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
- */
- private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
- if (isConnected())
- getBinder().disconnect();
- else
- stopSelf();
- }
- };
+ return builder.build();
+ }
+
+ /**
+ * Cancels the existing notification. If there is no active notification this method does nothing
+ */
+ private void cancelNotification() {
+ final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ nm.cancel(NOTIFICATION_ID);
+ }
+
+ /**
+ * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
+ */
+ private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
+ if (isConnected())
+ getBinder().disconnect();
+ else
+ stopSelf();
+ }
+ };
}
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTService.java
index 2e885ff7..9e97d53e 100644
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTService.java
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTService.java
@@ -30,9 +30,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import androidx.annotation.NonNull;
-import androidx.core.app.NotificationCompat;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
@@ -42,6 +40,9 @@ import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import no.nordicsemi.android.log.Logger;
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
import no.nordicsemi.android.nrftoolbox.R;
@@ -51,269 +52,300 @@ import no.nordicsemi.android.nrftoolbox.profile.LoggableBleManager;
import no.nordicsemi.android.nrftoolbox.wearable.common.Constants;
public class UARTService extends BleProfileService implements UARTManagerCallbacks {
- private static final String TAG = "UARTService";
+ private static final String TAG = "UARTService";
- public static final String BROADCAST_UART_TX = "no.nordicsemi.android.nrftoolbox.uart.BROADCAST_UART_TX";
- public static final String BROADCAST_UART_RX = "no.nordicsemi.android.nrftoolbox.uart.BROADCAST_UART_RX";
- public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.uart.EXTRA_DATA";
+ public static final String BROADCAST_UART_TX = "no.nordicsemi.android.nrftoolbox.uart.BROADCAST_UART_TX";
+ public static final String BROADCAST_UART_RX = "no.nordicsemi.android.nrftoolbox.uart.BROADCAST_UART_RX";
+ public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.uart.EXTRA_DATA";
- /** A broadcast message with this action and the message in {@link Intent#EXTRA_TEXT} will be sent t the UART device. */
- public final static String ACTION_SEND = "no.nordicsemi.android.nrftoolbox.uart.ACTION_SEND";
- /** A broadcast message with this action is triggered when a message is received from the UART device. */
- private final static String ACTION_RECEIVE = "no.nordicsemi.android.nrftoolbox.uart.ACTION_RECEIVE";
- /** Action send when user press the DISCONNECT button on the notification. */
- public final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.uart.ACTION_DISCONNECT";
- /** A source of an action. */
- public final static String EXTRA_SOURCE = "no.nordicsemi.android.nrftoolbox.uart.EXTRA_SOURCE";
- public final static int SOURCE_NOTIFICATION = 0;
- public final static int SOURCE_WEARABLE = 1;
- public final static int SOURCE_3RD_PARTY = 2;
+ /**
+ * A broadcast message with this action and the message in {@link Intent#EXTRA_TEXT} will be sent t the UART device.
+ */
+ public final static String ACTION_SEND = "no.nordicsemi.android.nrftoolbox.uart.ACTION_SEND";
+ /**
+ * A broadcast message with this action is triggered when a message is received from the UART device.
+ */
+ private final static String ACTION_RECEIVE = "no.nordicsemi.android.nrftoolbox.uart.ACTION_RECEIVE";
+ /**
+ * Action send when user press the DISCONNECT button on the notification.
+ */
+ public final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.uart.ACTION_DISCONNECT";
+ /**
+ * A source of an action.
+ */
+ public final static String EXTRA_SOURCE = "no.nordicsemi.android.nrftoolbox.uart.EXTRA_SOURCE";
+ public final static int SOURCE_NOTIFICATION = 0;
+ public final static int SOURCE_WEARABLE = 1;
+ public final static int SOURCE_3RD_PARTY = 2;
- private final static int NOTIFICATION_ID = 349; // random
- private final static int OPEN_ACTIVITY_REQ = 67; // random
- private final static int DISCONNECT_REQ = 97; // random
+ private final static int NOTIFICATION_ID = 349; // random
+ private final static int OPEN_ACTIVITY_REQ = 67; // random
+ private final static int DISCONNECT_REQ = 97; // random
- private GoogleApiClient mGoogleApiClient;
- private UARTManager mManager;
+ private GoogleApiClient mGoogleApiClient;
+ private UARTManager mManager;
- private final LocalBinder mBinder = new UARTBinder();
+ private final LocalBinder mBinder = new UARTBinder();
- public class UARTBinder extends LocalBinder implements UARTInterface {
- @Override
- public void send(final String text) {
- mManager.send(text);
- }
- }
+ public class UARTBinder extends LocalBinder implements UARTInterface {
+ @Override
+ public void send(final String text) {
+ mManager.send(text);
+ }
+ }
- @Override
- protected LocalBinder getBinder() {
- return mBinder;
- }
+ @Override
+ protected LocalBinder getBinder() {
+ return mBinder;
+ }
- @Override
- protected LoggableBleManager initializeManager() {
- return mManager = new UARTManager(this);
- }
+ @Override
+ protected LoggableBleManager initializeManager() {
+ return mManager = new UARTManager(this);
+ }
- @Override
- protected boolean shouldAutoConnect() {
- return true;
- }
+ @Override
+ protected boolean shouldAutoConnect() {
+ return true;
+ }
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- registerReceiver(mDisconnectActionBroadcastReceiver, new IntentFilter(ACTION_DISCONNECT));
- registerReceiver(mIntentBroadcastReceiver, new IntentFilter(ACTION_SEND));
+ registerReceiver(mDisconnectActionBroadcastReceiver, new IntentFilter(ACTION_DISCONNECT));
+ registerReceiver(mIntentBroadcastReceiver, new IntentFilter(ACTION_SEND));
- mGoogleApiClient = new GoogleApiClient.Builder(this)
- .addApi(Wearable.API)
- .build();
- mGoogleApiClient.connect();
- }
+ mGoogleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(Wearable.API)
+ .build();
+ mGoogleApiClient.connect();
+ }
- @Override
- public void onDestroy() {
- // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
- cancelNotification();
- unregisterReceiver(mDisconnectActionBroadcastReceiver);
- unregisterReceiver(mIntentBroadcastReceiver);
+ @Override
+ public void onDestroy() {
+ // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
+ stopForegroundService();
+ unregisterReceiver(mDisconnectActionBroadcastReceiver);
+ unregisterReceiver(mIntentBroadcastReceiver);
- mGoogleApiClient.disconnect();
+ mGoogleApiClient.disconnect();
- super.onDestroy();
- }
+ super.onDestroy();
+ }
- @Override
- protected void onRebind() {
- // when the activity rebinds to the service, remove the notification
- cancelNotification();
- }
+ @Override
+ protected void onRebind() {
+ stopForegroundService();
+ }
- @Override
- protected void onUnbind() {
- // when the activity closes we need to show the notification that user is connected to the sensor
- createNotification(R.string.uart_notification_connected_message, 0);
- }
+ @Override
+ protected void onUnbind() {
+ startForegroundService();
+ }
- @Override
- public void onDeviceConnected(@NonNull final BluetoothDevice device) {
- super.onDeviceConnected(device);
- sendMessageToWearables(Constants.UART.DEVICE_CONNECTED, notNull(getDeviceName()));
- }
+ @Override
+ public void onDeviceConnected(@NonNull final BluetoothDevice device) {
+ super.onDeviceConnected(device);
+ sendMessageToWearables(Constants.UART.DEVICE_CONNECTED, notNull(getDeviceName()));
+ }
- @Override
- protected boolean stopWhenDisconnected() {
- return false;
- }
+ @Override
+ protected boolean stopWhenDisconnected() {
+ return false;
+ }
- @Override
- public void onDeviceDisconnected(@NonNull final BluetoothDevice device) {
- super.onDeviceDisconnected(device);
- sendMessageToWearables(Constants.UART.DEVICE_DISCONNECTED, notNull(getDeviceName()));
- }
+ @Override
+ public void onDeviceDisconnected(@NonNull final BluetoothDevice device) {
+ super.onDeviceDisconnected(device);
+ sendMessageToWearables(Constants.UART.DEVICE_DISCONNECTED, notNull(getDeviceName()));
+ }
- @Override
- public void onLinkLossOccurred(@NonNull final BluetoothDevice device) {
- super.onLinkLossOccurred(device);
- sendMessageToWearables(Constants.UART.DEVICE_LINKLOSS, notNull(getDeviceName()));
- }
+ @Override
+ public void onLinkLossOccurred(@NonNull final BluetoothDevice device) {
+ super.onLinkLossOccurred(device);
+ sendMessageToWearables(Constants.UART.DEVICE_LINKLOSS, notNull(getDeviceName()));
+ }
- private String notNull(final String name) {
- if (!TextUtils.isEmpty(name))
- return name;
- return getString(R.string.not_available);
- }
+ private String notNull(final String name) {
+ if (!TextUtils.isEmpty(name))
+ return name;
+ return getString(R.string.not_available);
+ }
- @Override
- public void onDataReceived(final BluetoothDevice device, final String data) {
- final Intent broadcast = new Intent(BROADCAST_UART_RX);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_DATA, data);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ @Override
+ public void onDataReceived(final BluetoothDevice device, final String data) {
+ final Intent broadcast = new Intent(BROADCAST_UART_RX);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_DATA, data);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- // send the data received to other apps, e.g. the Tasker
- final Intent globalBroadcast = new Intent(ACTION_RECEIVE);
- globalBroadcast.putExtra(BluetoothDevice.EXTRA_DEVICE, getBluetoothDevice());
- globalBroadcast.putExtra(Intent.EXTRA_TEXT, data);
- sendBroadcast(globalBroadcast);
- }
+ // send the data received to other apps, e.g. the Tasker
+ final Intent globalBroadcast = new Intent(ACTION_RECEIVE);
+ globalBroadcast.putExtra(BluetoothDevice.EXTRA_DEVICE, getBluetoothDevice());
+ globalBroadcast.putExtra(Intent.EXTRA_TEXT, data);
+ sendBroadcast(globalBroadcast);
+ }
- @Override
- public void onDataSent(final BluetoothDevice device, final String data) {
- final Intent broadcast = new Intent(BROADCAST_UART_TX);
- broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
- broadcast.putExtra(EXTRA_DATA, data);
- LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
- }
+ @Override
+ public void onDataSent(final BluetoothDevice device, final String data) {
+ final Intent broadcast = new Intent(BROADCAST_UART_TX);
+ broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
+ broadcast.putExtra(EXTRA_DATA, data);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+ }
- /**
- * Sends the given message to all connected wearables. If the path is equal to {@link Constants.UART#DEVICE_DISCONNECTED} the service will be stopped afterwards.
- * @param path message path
- * @param message the message
- */
- private void sendMessageToWearables(final @NonNull String path, final @NonNull String message) {
- if(mGoogleApiClient.isConnected()) {
- new Thread(() -> {
- NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
- for(Node node : nodes.getNodes()) {
- Logger.v(getLogSession(), "[WEAR] Sending message '" + path + "' to " + node.getDisplayName());
- final MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path, message.getBytes()).await();
- if(result.getStatus().isSuccess()){
- Logger.i(getLogSession(), "[WEAR] Message sent");
- } else {
- Logger.w(getLogSession(), "[WEAR] Sending message failed: " + result.getStatus().getStatusMessage());
- Log.w(TAG, "Failed to send " + path + " to " + node.getDisplayName());
- }
- }
- if (Constants.UART.DEVICE_DISCONNECTED.equals(path))
- stopService();
- }).start();
- } else {
- if (Constants.UART.DEVICE_DISCONNECTED.equals(path))
- stopService();
- }
- }
+ /**
+ * Sends the given message to all connected wearables. If the path is equal to {@link Constants.UART#DEVICE_DISCONNECTED} the service will be stopped afterwards.
+ *
+ * @param path message path
+ * @param message the message
+ */
+ private void sendMessageToWearables(final @NonNull String path, final @NonNull String message) {
+ if (mGoogleApiClient.isConnected()) {
+ new Thread(() -> {
+ NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
+ for (Node node : nodes.getNodes()) {
+ Logger.v(getLogSession(), "[WEAR] Sending message '" + path + "' to " + node.getDisplayName());
+ final MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path, message.getBytes()).await();
+ if (result.getStatus().isSuccess()) {
+ Logger.i(getLogSession(), "[WEAR] Message sent");
+ } else {
+ Logger.w(getLogSession(), "[WEAR] Sending message failed: " + result.getStatus().getStatusMessage());
+ Log.w(TAG, "Failed to send " + path + " to " + node.getDisplayName());
+ }
+ }
+ if (Constants.UART.DEVICE_DISCONNECTED.equals(path))
+ stopService();
+ }).start();
+ } else {
+ if (Constants.UART.DEVICE_DISCONNECTED.equals(path))
+ stopService();
+ }
+ }
- /**
- * Creates the notification
- *
- * @param messageResId
- * message resource id. The message must have one String parameter,
- * f.e. <string name="name">%s is connected</string>
- * @param defaults
- * signals that will be used to notify the user
- */
- private void createNotification(final int messageResId, final int defaults) {
- final Intent parentIntent = new Intent(this, FeaturesActivity.class);
- parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final Intent targetIntent = new Intent(this, UARTActivity.class);
+ /**
+ * Sets the service as a foreground service
+ */
+ private void startForegroundService() {
+ // when the activity closes we need to show the notification that user is connected to the peripheral sensor
+ // We start the service as a foreground service as Android 8.0 (Oreo) onwards kills any running background services
+ final Notification notification = createNotification(R.string.uart_notification_connected_message, 0);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForeground(NOTIFICATION_ID, notification);
+ } else {
+ final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(NOTIFICATION_ID, notification);
+ }
+ }
- final Intent disconnect = new Intent(ACTION_DISCONNECT);
- disconnect.putExtra(EXTRA_SOURCE, SOURCE_NOTIFICATION);
- final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
+ /**
+ * Stops the service as a foreground service
+ */
+ private void stopForegroundService() {
+ // when the activity rebinds to the service, remove the notification and stop the foreground service
+ // on devices running Android 8.0 (Oreo) or above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ stopForeground(true);
+ } else {
+ cancelNotification();
+ }
+ }
- // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
- final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[] { parentIntent, targetIntent }, PendingIntent.FLAG_UPDATE_CURRENT);
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
- builder.setContentIntent(pendingIntent);
- builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
- builder.setSmallIcon(R.drawable.ic_stat_notify_uart);
- builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
- builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.uart_notification_action_disconnect), disconnectAction));
+ /**
+ * Creates the notification
+ *
+ * @param messageResId message resource id. The message must have one String parameter,
+ * f.e. <string name="name">%s is connected</string>
+ * @param defaults signals that will be used to notify the user
+ */
+ protected Notification createNotification(final int messageResId, final int defaults) {
+ final Intent parentIntent = new Intent(this, FeaturesActivity.class);
+ parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Intent targetIntent = new Intent(this, UARTActivity.class);
- final Notification notification = builder.build();
- final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(NOTIFICATION_ID, notification);
- }
+ final Intent disconnect = new Intent(ACTION_DISCONNECT);
+ disconnect.putExtra(EXTRA_SOURCE, SOURCE_NOTIFICATION);
+ final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
- /**
- * Cancels the existing notification. If there is no active notification this method does nothing
- */
- private void cancelNotification() {
- final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- nm.cancel(NOTIFICATION_ID);
- }
+ // both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
+ final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[]{parentIntent, targetIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
+ builder.setContentIntent(pendingIntent);
+ builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
+ builder.setSmallIcon(R.drawable.ic_stat_notify_uart);
+ builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
+ builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.uart_notification_action_disconnect), disconnectAction));
- /**
- * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
- */
- private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- final int source = intent.getIntExtra(EXTRA_SOURCE, SOURCE_NOTIFICATION);
- switch (source) {
- case SOURCE_NOTIFICATION:
- Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
- break;
- case SOURCE_WEARABLE:
- Logger.i(getLogSession(), "[WEAR] '" + Constants.ACTION_DISCONNECT + "' message received");
- break;
- }
- if (isConnected())
- getBinder().disconnect();
- else
- stopSelf();
- }
- };
+ return builder.build();
+ }
- /**
- * Broadcast receiver that listens for {@link #ACTION_SEND} from other apps. Sends the String or int content of the {@link Intent#EXTRA_TEXT} extra to the remote device.
- * The integer content will be sent as String (65 -> "65", not 65 -> "A").
- */
- private BroadcastReceiver mIntentBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- final boolean hasMessage = intent.hasExtra(Intent.EXTRA_TEXT);
- if (hasMessage) {
- String message = intent.getStringExtra(Intent.EXTRA_TEXT);
- if (message == null) {
- final int intValue = intent.getIntExtra(Intent.EXTRA_TEXT, Integer.MIN_VALUE); // how big is the chance of such data?
- if (intValue != Integer.MIN_VALUE)
- message = String.valueOf(intValue);
- }
+ /**
+ * Cancels the existing notification. If there is no active notification this method does nothing
+ */
+ private void cancelNotification() {
+ final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ nm.cancel(NOTIFICATION_ID);
+ }
- if (message != null) {
- final int source = intent.getIntExtra(EXTRA_SOURCE, SOURCE_3RD_PARTY);
- switch (source) {
- case SOURCE_WEARABLE:
- Logger.i(getLogSession(), "[WEAR] '" + Constants.UART.COMMAND + "' message received with data: \"" + message + "\"");
- break;
- case SOURCE_3RD_PARTY:
- default:
- Logger.i(getLogSession(), "[Broadcast] " + ACTION_SEND + " broadcast received with data: \"" + message + "\"");
- break;
- }
- mManager.send(message);
- return;
- }
- }
- // No data od incompatible type of EXTRA_TEXT
- if (!hasMessage)
- Logger.i(getLogSession(), "[Broadcast] " + ACTION_SEND + " broadcast received no data.");
- else
- Logger.i(getLogSession(), "[Broadcast] " + ACTION_SEND + " broadcast received incompatible data type. Only String and int are supported.");
- }
- };
+ /**
+ * This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
+ */
+ private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ final int source = intent.getIntExtra(EXTRA_SOURCE, SOURCE_NOTIFICATION);
+ switch (source) {
+ case SOURCE_NOTIFICATION:
+ Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
+ break;
+ case SOURCE_WEARABLE:
+ Logger.i(getLogSession(), "[WEAR] '" + Constants.ACTION_DISCONNECT + "' message received");
+ break;
+ }
+ if (isConnected())
+ getBinder().disconnect();
+ else
+ stopSelf();
+ }
+ };
+
+ /**
+ * Broadcast receiver that listens for {@link #ACTION_SEND} from other apps. Sends the String or int content of the {@link Intent#EXTRA_TEXT} extra to the remote device.
+ * The integer content will be sent as String (65 -> "65", not 65 -> "A").
+ */
+ private BroadcastReceiver mIntentBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ final boolean hasMessage = intent.hasExtra(Intent.EXTRA_TEXT);
+ if (hasMessage) {
+ String message = intent.getStringExtra(Intent.EXTRA_TEXT);
+ if (message == null) {
+ final int intValue = intent.getIntExtra(Intent.EXTRA_TEXT, Integer.MIN_VALUE); // how big is the chance of such data?
+ if (intValue != Integer.MIN_VALUE)
+ message = String.valueOf(intValue);
+ }
+
+ if (message != null) {
+ final int source = intent.getIntExtra(EXTRA_SOURCE, SOURCE_3RD_PARTY);
+ switch (source) {
+ case SOURCE_WEARABLE:
+ Logger.i(getLogSession(), "[WEAR] '" + Constants.UART.COMMAND + "' message received with data: \"" + message + "\"");
+ break;
+ case SOURCE_3RD_PARTY:
+ default:
+ Logger.i(getLogSession(), "[Broadcast] " + ACTION_SEND + " broadcast received with data: \"" + message + "\"");
+ break;
+ }
+ mManager.send(message);
+ return;
+ }
+ }
+ // No data od incompatible type of EXTRA_TEXT
+ if (!hasMessage)
+ Logger.i(getLogSession(), "[Broadcast] " + ACTION_SEND + " broadcast received no data.");
+ else
+ Logger.i(getLogSession(), "[Broadcast] " + ACTION_SEND + " broadcast received incompatible data type. Only String and int are supported.");
+ }
+ };
}