Template profile added

This commit is contained in:
Aleksander Nowakowski
2015-04-16 16:05:42 +02:00
parent f274ce7e0e
commit 29b35d48bc
18 changed files with 994 additions and 6 deletions

View File

@@ -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 {

View File

@@ -23,8 +23,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="no.nordicsemi.android.nrftoolbox"
android:installLocation="auto"
android:versionCode="30"
android:versionName="1.12.1" >
android:versionCode="31"
android:versionName="1.13.0" >
<uses-sdk
android:minSdkVersion="18"
@@ -59,6 +59,26 @@
android:label="@string/app_name"
android:launchMode="singleTask" >
</activity>
<!-- Template plugin activities -->
<!-- Remember to add your plug-in Activities to the Android Manifest file. -->
<activity
android:name="no.nordicsemi.android.nrftoolbox.template.TemplateActivity"
android:icon="@drawable/ic_template_feature"
android:label="@string/template_feature_title" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="no.nordicsemi.android.nrftoolbox.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="no.nordicsemi.android.nrftoolbox.template.settings.SettingsActivity"
android:label="@string/template_settings_title" />
<service
android:name="no.nordicsemi.android.nrftoolbox.template.TemplateService"
android:label="@string/template_feature_title" />
<!-- Plug-in activities -->
<activity
android:name="no.nordicsemi.android.nrftoolbox.dfu.DfuActivity"

View File

@@ -0,0 +1,61 @@
/*
* 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.parser;
import android.bluetooth.BluetoothGattCharacteristic;
import java.util.ArrayList;
import java.util.List;
// TODO this method may be used for developing purposes to log the data from your device using the nRF Logger application.
public class TemplateParser {
// TODO add some flags, if needed
private static final byte HEART_RATE_VALUE_FORMAT = 0x01; // 1 bit
/**
* This method converts the value of the characteristic to the String. The String is then logged in the nRF logger log session
* @param characteristic the characteristic to be parsed
* @return human readable value of the characteristic
*/
public static String parse(final BluetoothGattCharacteristic characteristic) {
int offset = 0;
final int flags = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++);
/*
* In the template we are using the HRM values as an example.
* false Heart Rate Value Format is set to UINT8. Units: beats per minute (bpm)
* true Heart Rate Value Format is set to UINT16. Units: beats per minute (bpm)
*/
final boolean value16bit = (flags & HEART_RATE_VALUE_FORMAT) > 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();
}
}

View File

@@ -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:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="no.nordicsemi.android.nrftoolbox.LAUNCHER" />
</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.

View File

@@ -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<TemplateService.TemplateBinder> {
@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<? extends BleProfileService> 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;
}
}

View File

@@ -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<TemplateManagerCallbacks> {
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<Request> initGatt(final BluetoothGatt gatt) {
final LinkedList<Request> requests = new LinkedList<>();
// TODO initialize your device, enable required notifications and indications, write what needs to be written to start working
requests.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
}
};
}

View File

@@ -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);
}

View File

@@ -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<TemplateManagerCallbacks> 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,<br />
* f.e. <code>&lt;string name="name"&gt;%s is connected&lt;/string&gt;</code>
* @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();
}
};
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HTSActivity" >
<!-- The size of text below must be fixed, therefore dp are used instead of sp -->
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/template_feature_title_long_margin"
android:rotation="270"
android:text="@string/template_feature_title_long"
android:textColor="@color/darkGray"
android:textSize="32dp"
android:textStyle="bold" />
<TextView
android:id="@+id/battery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="@dimen/feature_horizontal_margin"
android:layout_marginTop="@dimen/feature_device_name_margin_top"
android:background="@drawable/battery"
android:freezesText="true"
android:gravity="center"
android:text="@string/not_available"
android:textColor="#FFFFFF"
android:textSize="12sp" />
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetTextView
android:id="@+id/device_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/battery"
android:layout_marginTop="@dimen/feature_device_name_margin_top"
android:ellipsize="end"
android:freezesText="true"
android:maxLines="1"
android:text="@string/template_default_name"
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceLarge" />
<!-- Application section -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_vertical_margin_bottom"
android:layout_marginLeft="@dimen/feature_horizontal_margin"
android:layout_marginRight="@dimen/feature_horizontal_margin"
android:layout_marginTop="@dimen/feature_vertical_margin_top"
style="@style/Widget.List"
android:orientation="vertical" >
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
style="@style/Widget.ListTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/template_section_header" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/feature_section_padding" >
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetTextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/template_value_title" />
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
android:id="@+id/value"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:freezesText="true"
android:gravity="right"
android:text="@string/not_available_value"
android:textSize="36sp" />
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetTextView
android:id="@+id/value_unit"
android:layout_width="45dp"
android:layout_height="match_parent"
android:freezesText="true"
android:gravity="bottom"
android:text="@string/template_unit_bpm" />
</TableRow>
</TableLayout>
</LinearLayout>
<Button
android:id="@+id/action_connect"
style="@style/Widget.Connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/activity_vertical_margin_bottom"
android:onClick="onConnectClicked"
android:text="@string/action_connect" />
</RelativeLayout>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<resources>
<string name="template_feature_title">Template</string>
<string name="template_settings_title">Template Settings</string>
<string name="template_feature_title_long">TEMPLATE</string>
<dimen name="template_feature_title_long_margin">-62dp</dimen>
<string name="template_default_name">DEFAULT DEVICE</string>
<string name="template_section_header">Values</string>
<string name="template_value_title">Value</string>
<string name="template_unit_bpm">bpm</string>
<string name="template_notification_action_disconnect">Disconnect</string>
<string name="template_notification_connected_message">%s is connected.</string>
<string name="template_settings_units_title">Some data</string>
<string name="template_settings_units_summary">%s</string>
<string-array name="template_settings_units">
<item>Variant A</item>
<item>Variant B</item>
</string-array>
<string-array name="template_settings_units_values">
<item>0</item>
<item>1</item>
</string-array>
<string name="template_about_text">This is an empty profile that may be easily adopted to match customer requirements.
The Bluetooth Smart connection is handled by the Manager in Template Service.
\n\nBy default this profile displays the Heart Rate value.</string>
</resources>

View File

@@ -24,8 +24,8 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<ListPreference
android:title="@string/csc_settings_units_title"
android:summary="@string/csc_settings_units_summary"
android:title="@string/hts_settings_units_title"
android:summary="@string/hts_settings_units_summary"
android:defaultValue="0"
android:entries="@array/hts_settings_units"
android:entryValues="@array/hts_settings_units_values"

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<ListPreference
android:title="@string/template_settings_units_title"
android:summary="@string/template_settings_units_summary"
android:defaultValue="0"
android:entries="@array/template_settings_units"
android:entryValues="@array/template_settings_units_values"
android:key="settings_template_data" />
</PreferenceScreen>