diff --git a/app/build.gradle b/app/build.gradle index fb29324d..d47ddaf3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "no.nordicsemi.android.nrftoolbox" minSdkVersion 18 targetSdkVersion 22 - versionCode 30 - versionName "1.12.1" + versionCode 31 + versionName "1.13.0" } buildTypes { release { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index acf0f856..4383534b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,8 +23,8 @@ + android:versionCode="31" + android:versionName="1.13.0" > + + + + + + + + + + + + + 0; + + // heart rate value is 8 or 16 bit long + int value = characteristic.getIntValue(value16bit ? BluetoothGattCharacteristic.FORMAT_UINT16 : BluetoothGattCharacteristic.FORMAT_UINT8, offset++); // bits per minute + if (value16bit) + offset++; + + // TODO parse more data + + final StringBuilder builder = new StringBuilder(); + builder.append("Template Measurement: ").append(value).append(" bpm"); + return builder.toString(); + } +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/Readme.txt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/Readme.txt new file mode 100644 index 00000000..aa3ffa43 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/Readme.txt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + nRF Toolbox demonstrates how to implement the Bluetooth Smart features in an Android application. + It consists of number of profiles, like Heart Rate, Blood Pressure etc. that may be use as is to communicate with real devices. + They use the Bluetooth SIG adopted profiles, that may be found here: https://developer.bluetooth.org/gatt/profiles/Pages/ProfilesHome.aspx + + The Template Profile has been created to give a quick start with implementing proprietary services. Just start modifying 4 classes inside + the template package to implement features you need. + + Below you will find a short step-by-step tutorial: + + 1. The template consists of the following files: + - TemplateActivity - the main class that is responsible for managing the view of your profile + - TemplateService - the service that is started whenever you connect to a device. I handles the Bluetooth Smart communication using the... + - TemplateManager - the manager that handles all the BLE logic required to communicate with the device. The TemplateManager derives from + the BleManager which handles most of the event itself and propagates the data-relevant to deriving class. You don't + have to, or even shouldn't modify the BleManager (unless you want to change the default behaviour). + - TemplateManagerCallbacks - the interface with a list of callbacks that the TemplateManager can call. Each method is usually related to one + BLE event, e.g. receiving a new value of the characteristic.\ + - TemplateParser - an optional class in the .parser package that is responsible for converting the characteristic value to String. + This is used only for debugging. The String returned by the parse(..) method is then logged into the nRF Logger application + (if such installed). + - /settings/SettingsActivity and /settings/SettingsFragment - classes used to present user preferences. A stub implementation in the template. + - /res/layout/activity_feature_template.xml - the layout file for the TemplateActivity + - /res/values/strings_template.xml - a set of strings used in the layout file + - /res/xml/settings/template.xml - the user settings configuration + - /res/drawable/(x)hdpi/ic_template_feature.png - the template profile icon (HDPI, XHDPI). Please, keep the files size. + - /res/drawable/(x)hdpi/ic_stat_notify_template - the icon that is used in the notification + +2. The communication between the components goes as follows: + - User clicks the CONNECT button and selects a target device on TemplateActivity. + - The base class of the TemplateActivity starts the service given by getServiceClass() method. + - The service starts and initializes the TemplateManager. TemplateActivity binds to the service and is being given the TemplateBinder object (the service API) as a result. + - The manager connects to the device using Bluetooth Smart and discovers its services. + - The manager initializes the device. Initialization is done using the list of actions given by the initGatt(..) method in the TemplateManager. + Initialization usually contains enabling notifications, writing some initial values etc. + - When initialization is complete the manager calls the onDeviceReady() callback. + - The service sends the BROADCAST_DEVICE_READY broadcast to the activity. Communication from the Service to the Activity is always done using the LocalBroadcastManager broadcasts. + - The base class of the TemplateActivity listens to the broadcasts and calls appropriate methods. + + - When a custom event occurs, for example a notification is received, the manager parses the incoming data and calls the proper callback. + - The callback implementation in the TemplateService sends a broadcast message with values given in parameters. + - The TemplateActivity, which had registered a broadcast receiver before, listens to the broadcasts, reads the values and present them to users. + + - Communication Activity->Service is done using the API in the TemplateBinder. You may find the example of how to use it in the ProximityActivity. + +3. Please read the files listed above and the TODO messages for more information what to modify in the files. + +4. Remember to add your activities and the service in the AndroidManifest.xml file. The nRF Toolbox lists all activities with the following intent filter: + + + + + +5. Feel free to rename the nRF Toolbox application (/res/values/strings.xml ->app_name), change the toolbar colors (/res/values/color.xml -> actionBarColor, actionBarColorDark). + In order to remove unused profiles from the main FeaturesActivity just comment out their intent-filter tags in the AndroidManifest.xml file. diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateActivity.java new file mode 100644 index 00000000..e7197cd6 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateActivity.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.nrftoolbox.template; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; +import android.view.Menu; +import android.widget.TextView; + +import java.util.UUID; + +import no.nordicsemi.android.nrftoolbox.R; +import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; +import no.nordicsemi.android.nrftoolbox.profile.BleProfileServiceReadyActivity; +import no.nordicsemi.android.nrftoolbox.template.settings.SettingsActivity; + +/** + * Modify the Template Activity to match your needs. + */ +public class TemplateActivity extends BleProfileServiceReadyActivity { + @SuppressWarnings("unused") + private final String TAG = "TemplateActivity"; + + // TODO change view references to match your need + private TextView mValueView; + private TextView mValueUnitView; + + @Override + protected void onCreateView(final Bundle savedInstanceState) { + // TODO modify the layout file(s). By default the activity shows only one field - the Heart Rate value as a sample + setContentView(R.layout.activity_feature_template); + setGUI(); + } + + private void setGUI() { + // TODO assign your views to fields + mValueView = (TextView) findViewById(R.id.value); + mValueUnitView = (TextView) findViewById(R.id.value_unit); + } + + @Override + protected void onInitialize(final Bundle savedInstanceState) { + LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, makeIntentFilter()); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver); + } + + @Override + protected void setDefaultUI() { + // TODO clear your UI + mValueView.setText(R.string.not_available_value); + } + + @Override + protected void onServiceBinded(final TemplateService.TemplateBinder binder) { + // not used + } + + @Override + protected void onServiceUnbinded() { + // not used + } + + @Override + protected int getLoggerProfileTitle() { + return R.string.template_feature_title; + } + + @Override + protected int getAboutTextId() { + return R.string.template_about_text; + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.settings_and_about, menu); + return true; + } + + @Override + protected boolean onOptionsItemSelected(final int itemId) { + switch (itemId) { + case R.id.action_settings: + final Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + break; + } + return true; + } + + @Override + protected int getDefaultDeviceName() { + return R.string.template_default_name; + } + + @Override + protected UUID getFilterUUID() { + // TODO this method may return the UUID of the service that is required to be in the advertisement packet of a device in order to be listed on the Scanner dialog. + // If null is returned no filtering is done. + return TemplateManager.SERVICE_UUID; + } + + @Override + protected Class getServiceClass() { + return TemplateService.class; + } + + @Override + public void onServicesDiscovered(boolean optionalServicesFound) { + // this may notify user or show some views + } + + private void setValueOnView(final int value) { + // TODO assign the value to a view + mValueView.setText(String.valueOf(value)); + } + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + final String action = intent.getAction(); + + if (TemplateService.BROADCAST_TEMPLATE_MEASUREMENT.equals(action)) { + final int value = intent.getIntExtra(TemplateService.EXTRA_DATA, 0); + // Update GUI + setValueOnView(value); + } + } + }; + + private static IntentFilter makeIntentFilter() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TemplateService.BROADCAST_TEMPLATE_MEASUREMENT); + return intentFilter; + } +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManager.java new file mode 100644 index 00000000..1c731c78 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManager.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.nrftoolbox.template; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.content.Context; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.UUID; + +import no.nordicsemi.android.log.Logger; +import no.nordicsemi.android.nrftoolbox.parser.TemplateParser; +import no.nordicsemi.android.nrftoolbox.profile.BleManager; + +/** + * Modify to template manager to match your requirements. + */ +public class TemplateManager extends BleManager { + private static final String TAG = "TemplateManager"; + + /** The service UUID */ + public final static UUID SERVICE_UUID = UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb"); // TODO change the UUID to your match your service + /** The characteristic UUID */ + private static final UUID MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A37-0000-1000-8000-00805f9b34fb"); // TODO change the UUID to your match your characteristic + + // TODO add more services and characteristics, if required + private BluetoothGattCharacteristic mCharacteristic; + + public TemplateManager(final Context context) { + super(context); + } + + @Override + protected BleManagerGattCallback getGattCallback() { + return mGattCallback; + } + + /** + * BluetoothGatt callbacks for connection/disconnection, service discovery, receiving indication, etc + */ + private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() { + + @Override + protected Queue initGatt(final BluetoothGatt gatt) { + final LinkedList requests = new LinkedList<>(); + // TODO initialize your device, enable required notifications and indications, write what needs to be written to start working + requests.push(Request.newEnableNotificationsRequest(mCharacteristic)); + return requests; + } + + @Override + protected boolean isRequiredServiceSupported(final BluetoothGatt gatt) { + final BluetoothGattService service = gatt.getService(SERVICE_UUID); + if (service != null) { + mCharacteristic = service.getCharacteristic(MEASUREMENT_CHARACTERISTIC_UUID); + } + return mCharacteristic != null; + } + + @Override + protected void onDeviceDisconnected() { + mCharacteristic = null; + } + + // TODO implement data handlers. Methods below are called after the initialization is complete. + + @Override + protected void onDeviceReady() { + super.onDeviceReady(); + + // TODO initialization is now ready. The activity is being notified using TemplateManagerCallbacks#onDeviceReady() method. + // This method may be removed from this class if not required as the super class implementation handles this event. + } + + @Override + protected void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + // TODO this method is called when a notification has been received + // This method may be removed from this class if not required + + if (mLogSession != null) + Logger.a(mLogSession, TemplateParser.parse(characteristic)); + + int value; + final int flags = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); + if ((flags & 0x01) > 0) { + value = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1); + } else { + value = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1); + } + //This will send callback to the Activity when new value is received from HR device + mCallbacks.onSampleValueReceived(value); + } + + @Override + protected void onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + // TODO this method is called when an indication has been received + // This method may be removed from this class if not required + } + + @Override + protected void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + // TODO this method is called when the characteristic has been read + // This method may be removed from this class if not required + } + + @Override + protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + // TODO this method is called when the characteristic has been written + // This method may be removed from this class if not required + } + }; + + +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManagerCallbacks.java new file mode 100644 index 00000000..125c25f5 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManagerCallbacks.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.nrftoolbox.template; + +import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; + +/** + * Interface {@link TemplateManagerCallbacks} must be implemented by {@link TemplateActivity} in order to receive callbacks from {@link TemplateManager} + */ +public interface TemplateManagerCallbacks extends BleManagerCallbacks { + + // TODO add more callbacks. Callbacks are called when a data has been received/written to a remote device. This is the way how the manager notifies the activity about this event. + + /** + * Called when a value is received. + * + * @param value + * the new value + */ + public void onSampleValueReceived(int value); + +} 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 new file mode 100644 index 00000000..6b0ba5d4 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.nrftoolbox.template; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.support.v4.content.LocalBroadcastManager; + +import no.nordicsemi.android.log.Logger; +import no.nordicsemi.android.nrftoolbox.FeaturesActivity; +import no.nordicsemi.android.nrftoolbox.R; +import no.nordicsemi.android.nrftoolbox.profile.BleManager; +import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; + +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"; + + 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 TemplateManager mManager; + + private final LocalBinder mBinder = new TemplateBinder(); + + /** + * This local binder is an interface for the bonded activity to operate with the HTS sensor + */ + public class TemplateBinder extends LocalBinder { + // empty + // TODO add some service API that mey be used from the Activity + + // public void setLights(final boolean on) { + // Logger.v(getLogSession(), "Light set to: " + on); + // mManager.setLights(on); + // } + } + + @Override + protected LocalBinder getBinder() { + return mBinder; + } + + @Override + protected BleManager initializeManager() { + return mManager = new TemplateManager(this); + } + + @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); + + super.onDestroy(); + } + + @Override + protected void onRebind() { + // when the activity rebinds to the service, remove the notification + cancelNotification(); + } + + @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 onServiceStarted() { + // logger is now available. Assign it to the manager + mManager.setLogger(getLogSession()); + } + + @Override + public void onSampleValueReceived(final int value) { + final Intent broadcast = new Intent(BROADCAST_TEMPLATE_MEASUREMENT); + broadcast.putExtra(EXTRA_DATA, value); + LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); + + if (!mBinded) { + // Here we may update the notification to display the current value. + // TODO modify the notification here + } + } + + /** + * 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); + + final Intent disconnect = new Intent(ACTION_DISCONNECT); + final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT); + + // 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 Notification.Builder builder = new Notification.Builder(this).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(R.drawable.ic_action_bluetooth, getString(R.string.template_notification_action_disconnect), disconnectAction); + + final Notification notification = builder.build(); + final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + nm.notify(NOTIFICATION_ID, notification); + } + + /** + * 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/settings/SettingsActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/settings/SettingsActivity.java new file mode 100644 index 00000000..b64b1482 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/settings/SettingsActivity.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.nrftoolbox.template.settings; + +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.MenuItem; + +public class SettingsActivity extends ActionBarActivity { + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + // Display the fragment as the main content. + getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit(); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + } + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/settings/SettingsFragment.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/settings/SettingsFragment.java new file mode 100644 index 00000000..947f8d77 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/settings/SettingsFragment.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.nrftoolbox.template.settings; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import no.nordicsemi.android.nrftoolbox.R; + +public class SettingsFragment extends PreferenceFragment { + public static final String SETTINGS_DATA = "settings_template_data"; // TODO values matching those in settings_template.xml file in /res/xml + public static final int SETTINGS_VARIANT_A = 0; + public static final int SETTINGS_VARIANT_B = 1; + public static final int SETTINGS_VARIANT_DEFAULT = SETTINGS_VARIANT_A; + + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.settings_template); + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_template.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_template.png new file mode 100644 index 00000000..fc09568b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stat_notify_template.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_template_feature.png b/app/src/main/res/drawable-hdpi/ic_template_feature.png new file mode 100644 index 00000000..fef8a417 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_template_feature.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_template.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_template.png new file mode 100644 index 00000000..8b4925d3 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stat_notify_template.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_template_feature.png b/app/src/main/res/drawable-xhdpi/ic_template_feature.png new file mode 100644 index 00000000..ac88d4a3 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_template_feature.png differ diff --git a/app/src/main/res/layout/activity_feature_template.xml b/app/src/main/res/layout/activity_feature_template.xml new file mode 100644 index 00000000..6e282fe2 --- /dev/null +++ b/app/src/main/res/layout/activity_feature_template.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +