diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/BluetoothLeService.java b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/BluetoothLeService.java index da7ef7e4bd9b75a4b0f32de13ec17233e00a8444..1b54ac2b11a80136c2533739b21ee803ac940899 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/BluetoothLeService.java +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/BluetoothLeService.java @@ -43,9 +43,6 @@ public class BluetoothLeService extends Service { //private BluetoothGatt mBluetoothGatt; private int mConnectionState = STATE_DISCONNECTED; -// private LinkedList<BluetoothGattCharacteristic> characteristicsToRead = new LinkedList<>(); -// private LinkedList<BluetoothGattCharacteristic> characteristicsToWrite = new LinkedList<>(); - // Sweetblue private BleManager mBleManager; private BleDevice mDevice; @@ -73,78 +70,6 @@ public class BluetoothLeService extends Service { public final static String EXTRA_DATA_UUID = "com.example.bluetooth.le.DATA_UUID"; -// // Implements callback methods for GATT events that the app cares about. For example, -// // connection change and services discovered. -// private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { -// @Override -// public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { -// String intentAction; -// if (newState == BluetoothProfile.STATE_CONNECTED) { -// intentAction = ACTION_GATT_CONNECTED; -// mConnectionState = STATE_CONNECTED; -// broadcastUpdate(intentAction); -// Log.i(TAG, "Connected to GATT server."); -// // Attempts to discover services after successful connection. -// Log.i(TAG, "Attempting to start service discovery:" + -// mBluetoothGatt.discoverServices()); -// -// } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { -// intentAction = ACTION_GATT_DISCONNECTED; -// mConnectionState = STATE_DISCONNECTED; -// Log.i(TAG, "Disconnected from GATT server."); -// broadcastUpdate(intentAction); -// } -// } -// -// @Override -// public void onServicesDiscovered(BluetoothGatt gatt, int status) { -// if (status == BluetoothGatt.GATT_SUCCESS) { -// broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); -// } else { -// Log.w(TAG, "onServicesDiscovered received: " + status); -// } -// } -// -// @Override -// public void onCharacteristicRead(BluetoothGatt gatt, -// BluetoothGattCharacteristic characteristic, -// int status) { -// if (status == BluetoothGatt.GATT_SUCCESS) { -// broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); -// -// Log.d(TAG, "onCharacteristicRead: data received from " + characteristic.getUuid()); -// } -// -// // TODO : test is the current characteristic read is the same that returned data -// synchronized (characteristicsToRead) { -// characteristicsToRead.poll(); // Remove the characteristic that has just been read -// if (!characteristicsToRead.isEmpty()) { -// //Log.d(TAG, "onCharacteristicRead: restart read with " + characteristicsToRead.getFirst().getUuid()); -// mBluetoothGatt.readCharacteristic(characteristicsToRead.getFirst()); -// } -// } -// } -// -// @Override -// public void onCharacteristicChanged(BluetoothGatt gatt, -// BluetoothGattCharacteristic characteristic) { -// broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); -// } -// -// @Override -// public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { -// super.onCharacteristicWrite(gatt, characteristic, status); -// -// synchronized (characteristicsToWrite) { -// characteristicsToWrite.poll(); // Remove the characteristic that has just been read -// if (!characteristicsToWrite.isEmpty()) { -// mBluetoothGatt.writeCharacteristic(characteristicsToRead.getFirst()); -// } -// } -// } -// }; - - private final BleDevice.StateListener stateListener = new BleDevice.StateListener() { @Override public void onEvent(StateEvent e) { @@ -239,21 +164,6 @@ public class BluetoothLeService extends Service { * @return Return true if the initialization is successful. */ public boolean initialize() { -// // For API level 18 and above, get a reference to BluetoothAdapter through -// // BluetoothManager. -// if (mBluetoothManager == null) { -// mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); -// if (mBluetoothManager == null) { -// Log.e(TAG, "Unable to initialize BluetoothManager."); -// return false; -// } -// } -// -// mBluetoothAdapter = mBluetoothManager.getAdapter(); -// if (mBluetoothAdapter == null) { -// Log.e(TAG, "Unable to obtain a BluetoothAdapter."); -// return false; -// } // Get Bluetooth manager for this device mBleManager = BleManager.get(this, mBleManagerConfig); @@ -263,20 +173,6 @@ public class BluetoothLeService extends Service { public boolean connect(BleDevice device) { -// characteristicsToRead.clear(); - -// // Previously connected device. Try to reconnect. -// if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) -// && mBluetoothGatt != null) { -// Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); -// if (mBluetoothGatt.connect()) { -// mConnectionState = STATE_CONNECTING; -// return true; -// } else { -// return false; -// } -// } - // If already connected, drop the connection if (mDevice != null) { mDevice.disconnect(); @@ -286,11 +182,6 @@ public class BluetoothLeService extends Service { mDevice = device; mDevice.connect(stateListener); -// // We want to directly connect to the device, so we are setting the autoConnect -// // parameter to false. -// mBluetoothGatt = device.connectGatt(this, false, mGattCallback); - Log.d(TAG, "Trying to create a new connection."); - mConnectionState = STATE_CONNECTING; return true; } @@ -302,10 +193,6 @@ public class BluetoothLeService extends Service { * callback. */ public void disconnect() { -// if (mBluetoothAdapter == null || mBluetoothGatt == null) { -// Log.w(TAG, "BluetoothAdapter not initialized"); -// return; -// } // If connected, drop the connection if (mDevice != null) { mDevice.disconnect(); @@ -318,12 +205,6 @@ public class BluetoothLeService extends Service { * released properly. */ public void close() { -// if (mBluetoothGatt == null) { -// return; -// } -// mBluetoothGatt.close(); -// mBluetoothGatt = null; - if (mDevice != null) { mDevice.disconnect(); mDevice = null; @@ -337,36 +218,15 @@ public class BluetoothLeService extends Service { * * @param characteristic The characteristic to read from. */ - public void readCharacteristic(BluetoothGattCharacteristic characteristic) { -// if (mBluetoothAdapter == null || mBluetoothGatt == null) { -// Log.w(TAG, "BluetoothAdapter not initialized"); -// return; -// } - + public void readCharacteristic(UUID serviceUUID, UUID charUUID) { if (mConnectionState != STATE_CONNECTED) { Log.w(TAG, "Not connected to the device"); return; } - if (!mDevice.read(characteristic.getUuid(), readWriteListener).wasSuccess()) { - Log.d(TAG, "writeCharacteristic: characteristic not writable" + characteristic.getUuid()); + if (!mDevice.read(serviceUUID, charUUID, readWriteListener).wasSuccess()) { + Log.d(TAG, "writeCharacteristic: characteristic not readable" + charUUID); } - -// synchronized (characteristicsToRead) { -// final int charaProp = characteristic.getProperties(); -// if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) { -// if (characteristicsToRead.isEmpty()) { -// characteristicsToRead.add(characteristic); -// mBluetoothGatt.readCharacteristic(characteristic); -// //Log.d(TAG, "readCharacteristic: linkedlist empty : " + characteristic.getUuid()); -// } else { -// characteristicsToRead.add(characteristic); -// //Log.d(TAG, "readCharacteristic: queuing : " + characteristic.getUuid()); -// } -// } else { -// Log.d(TAG, "readCharacteristic: characteristic not readable" + characteristic.getUuid()); -// } -// } } /** @@ -376,34 +236,14 @@ public class BluetoothLeService extends Service { * * @param characteristic The characteristic to write to. */ - public void writeCharacteristic(BluetoothGattCharacteristic characteristic) { -// if (mBluetoothAdapter == null || mBluetoothGatt == null) { -// Log.w(TAG, "BluetoothAdapter not initialized"); -// return; -// } -// -// synchronized (characteristicsToWrite) { -// final int charaProp = characteristic.getProperties(); -// if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) { -// if (characteristicsToWrite.isEmpty()) { -// characteristicsToWrite.add(characteristic); -// mBluetoothGatt.writeCharacteristic(characteristic); -// } else { -// characteristicsToWrite.add(characteristic); -// } -// } else { -// Log.d(TAG, "writeCharacteristic: characteristic not writable" + characteristic.getUuid()); -// } -// } - - + public void writeCharacteristic(UUID serviceUUID, UUID charUUID, byte[] data) { if (mConnectionState != STATE_CONNECTED) { Log.w(TAG, "Not connected to the device"); return; } - if (!mDevice.read(characteristic.getUuid(), readWriteListener).wasSuccess()) { - Log.d(TAG, "writeCharacteristic: characteristic not writable" + characteristic.getUuid()); + if (!mDevice.write(serviceUUID, charUUID, data, readWriteListener).wasSuccess()) { + Log.d(TAG, "writeCharacteristic: characteristic not writable" + charUUID); } } @@ -415,11 +255,6 @@ public class BluetoothLeService extends Service { */ public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { -// if (mBluetoothAdapter == null || mBluetoothGatt == null) { -// Log.w(TAG, "BluetoothAdapter not initialized"); -// return; -// } -// mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); if (mConnectionState != STATE_CONNECTED) { Log.w(TAG, "Not connected to the device"); @@ -436,41 +271,6 @@ public class BluetoothLeService extends Service { } -// /** -// * Enables or disables indications on a given characteristic. -// * -// * @param characteristic Characteristic to act on. -// * @param enabled If true, enable indication. False otherwise. -// */ -// public void setCharacteristicIndication(BluetoothGattCharacteristic characteristic, -// boolean enabled) { -//// if (mBluetoothAdapter == null || mBluetoothGatt == null) { -//// Log.w(TAG, "BluetoothAdapter not initialized"); -//// return; -//// } -//// mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); -//// -//// // This is specific to WEAZARD SERVICE -//// if (UUID.fromString(SampleGattAttributes.DISCOVERYTREE_INDICATION_TREE_DATA).equals(characteristic.getUuid())) { -//// BluetoothGattDescriptor descriptor = characteristic.getDescriptor( -//// UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); -//// descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); -//// mBluetoothGatt.writeDescriptor(descriptor); -//// } -// -// if (mConnectionState != STATE_CONNECTED) { -// Log.w(TAG, "Not connected to the device"); -// return; -// } -// -// if (mDevice.isNotifyEnabled(characteristic.getUuid())) { -// mDevice.enableNotify(characteristic.getUuid()); -// } else { -// Log.d(TAG, "setCharacteristicNotification: characteristic notification already enabled " + characteristic.getUuid()); -// } -// -// } - /** * Retrieves a list of supported GATT services on the connected device. This should be * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully. @@ -478,10 +278,6 @@ public class BluetoothLeService extends Service { * @return A {@code List} of supported services. */ public List<BluetoothGattService> getSupportedGattServices() { -// if (mBluetoothGatt == null) return null; -// -// return mBluetoothGatt.getServices(); - if (mConnectionState != STATE_CONNECTED) { Log.w(TAG, "Not connected to the device"); return null; diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/Constants.java b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/Constants.java index 3df00ad8cd1a862daa5a031a84efd77b1e21b151..cbaf950eaff1b55cb6eae036795fee6a2b3d54c9 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/Constants.java +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/Constants.java @@ -25,7 +25,7 @@ public class Constants { /** * UUID identified with this app - set as Service UUID for BLE Advertisements. - * + * <p> * Bluetooth requires a certain format for UUIDs associated with Services. * The official specification can be found here: * {@link https://www.bluetooth.org/en-us/specification/assigned-numbers/service-discovery} diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/DeviceConfigurationFragment.java b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/DeviceConfigurationFragment.java index 5d62cf9e98b1ffec2b30a6b74dfc996d605af2a0..5be4b084be41b669cbbd562bc9f7f716d056e2f3 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/DeviceConfigurationFragment.java +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/DeviceConfigurationFragment.java @@ -12,8 +12,10 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; +import android.widget.Spinner; import java.util.ArrayList; import java.util.Arrays; @@ -21,8 +23,14 @@ import java.util.HashMap; import java.util.List; import java.util.UUID; -import static android.content.Context.BIND_AUTO_CREATE; -import static com.example.android.bluetoothadvertisements.SampleGattAttributes.*; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.DEVICE_INFOS_CHARACTERISTIC; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.DEVICE_NAME_CHARACTERISTIC; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.FIRST_NAME_CHARACTERISTIC; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.LAST_NAME_CHARACTERISTIC; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.NAMESPACE_CHARACTERISTIC; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.TREE_DATA_CHARACTERISTIC; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.TX_POWER_CHARACTERISTIC; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.WEAZARD_SERVICE; /** * Created by David on 01.12.2016. @@ -30,19 +38,17 @@ import static com.example.android.bluetoothadvertisements.SampleGattAttributes.* public class DeviceConfigurationFragment extends Fragment { private final static String TAG = com.example.android.bluetoothadvertisements.DeviceConfigurationFragment.class.getSimpleName(); - - private BluetoothLeService mBluetoothLeService; - - private List<BluetoothGattService> supportedServices; - private final String LIST_NAME = "NAME"; private final String LIST_UUID = "UUID"; + private BluetoothLeService mBluetoothLeService; + private List<BluetoothGattService> supportedServices; private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics; private EditText etDeviceName; private EditText etNamespace; private EditText etFirstName; private EditText etLastName; + private Spinner spinnerTxPower; private EditText etGSR; private EditText etWeazardInformation; private EditText etNbScans; @@ -58,10 +64,10 @@ public class DeviceConfigurationFragment extends Fragment { private String mNbScans; private String mWeazardDepth; private String mWeazardId; - private Button btnReadConfig; private Button btnWriteConfig; private Button btnEnableDiscoveryTreeNotifs; + private Button btnEnableDeviceInfosNotifs; // Handles various events fired by the Service. // ACTION_GATT_CONNECTED: connected to a GATT server. @@ -74,11 +80,7 @@ public class DeviceConfigurationFragment extends Fragment { public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { - - } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { - - } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { + if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { supportedServices = mBluetoothLeService.getSupportedGattServices(); extractCharacteristicsFromGattServices(supportedServices); @@ -88,18 +90,24 @@ public class DeviceConfigurationFragment extends Fragment { String charUuid = intent.getStringExtra(BluetoothLeService.EXTRA_DATA_UUID); switch (charUuid) { - case WEAZARD_NAMESPACE_CHARACTERISTIC: + case NAMESPACE_CHARACTERISTIC: Integer hexData = Integer.parseInt(charData, 16); etNamespace.setText(hexData.toString()); break; - case WEAZARD_INFOS_CHARACTERISTIC: + case DEVICE_INFOS_CHARACTERISTIC: etWeazardInformation.setText(charData); - etNbScans.setText(charData.substring(0,2)); - etWeazardId.setText(charData.substring(2,4)); - etWeazardDepth.setText(charData.substring(4,6)); + etNbScans.setText(charData.substring(0, 2)); + etWeazardId.setText(charData.substring(2, 4)); + etWeazardDepth.setText(charData.substring(4, 6)); break; case TX_POWER_CHARACTERISTIC: - //etTxPower.setText(StringUtils.convertHexToString(charData)); + ArrayAdapter<String> txPowerArray = new ArrayAdapter<String>(getActivity(), + android.R.layout.simple_spinner_dropdown_item, + getResources().getStringArray(R.array.tx_power_array)); + byte[] txPower = StringUtils.hexStringToByteArray(charData); + String str = String.valueOf(txPower[0]); + int pos = txPowerArray.getPosition(String.valueOf(txPower[0])); + spinnerTxPower.setSelection(pos); break; case DEVICE_NAME_CHARACTERISTIC: etDeviceName.setText(StringUtils.convertHexToString(charData)); @@ -119,6 +127,15 @@ public class DeviceConfigurationFragment extends Fragment { public DeviceConfigurationFragment() { } + private static IntentFilter makeGattUpdateIntentFilter() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); + return intentFilter; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -140,6 +157,7 @@ public class DeviceConfigurationFragment extends Fragment { etNbScans = (EditText) view.findViewById(R.id.etxt_nb_scan_5sec); etWeazardId = (EditText) view.findViewById(R.id.etxt_weazard_id); etWeazardDepth = (EditText) view.findViewById(R.id.etxt_weazard_depth); + spinnerTxPower = (Spinner) view.findViewById(R.id.spin_tx_power); btnReadConfig = (Button) view.findViewById(R.id.read_config_weazard_device_btn); btnReadConfig.setOnClickListener(new View.OnClickListener() { @@ -164,7 +182,16 @@ public class DeviceConfigurationFragment extends Fragment { @Override public void onClick(View v) { - enableDiscoveryTreeNotifications(); + enableNotifications(UUID.fromString(WEAZARD_SERVICE), UUID.fromString(TREE_DATA_CHARACTERISTIC)); + } + }); + + btnEnableDeviceInfosNotifs = (Button) view.findViewById(R.id.enable_device_infos_notifications_btn); + btnEnableDeviceInfosNotifs.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + enableNotifications(UUID.fromString(WEAZARD_SERVICE), UUID.fromString(DEVICE_INFOS_CHARACTERISTIC)); } }); @@ -180,7 +207,7 @@ public class DeviceConfigurationFragment extends Fragment { public void onResume() { super.onResume(); getActivity().registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter()); - + readAllCharacteristics(); } @Override @@ -195,76 +222,80 @@ public class DeviceConfigurationFragment extends Fragment { } - public void updateDeviceInformations(BluetoothLeService bluetoothLeService) { - mBluetoothLeService = bluetoothLeService; - } - public void readAllCharacteristics() { - - // TODO : move it from there... - supportedServices = mBluetoothLeService.getSupportedGattServices(); - extractCharacteristicsFromGattServices(supportedServices); - List<String> characteristicsToRead = Arrays.asList( DEVICE_NAME_CHARACTERISTIC, - WEAZARD_NAMESPACE_CHARACTERISTIC, - WEAZARD_INFOS_CHARACTERISTIC, - TX_POWER_CHARACTERISTIC, + NAMESPACE_CHARACTERISTIC, FIRST_NAME_CHARACTERISTIC, - LAST_NAME_CHARACTERISTIC); + LAST_NAME_CHARACTERISTIC, + DEVICE_INFOS_CHARACTERISTIC, + TX_POWER_CHARACTERISTIC); BluetoothGattCharacteristic gattChar; for (String charToRead : characteristicsToRead) { - if ((gattChar = findCharacteristicInCharacteristicsSupported(WEAZARD_SERVICE, charToRead)) == null) { + gattChar = findCharacteristicInCharacteristicsSupported(UUID.fromString(WEAZARD_SERVICE), UUID.fromString(charToRead)); + if (gattChar == null) { Log.d(TAG, "readAllCharacteristics: characteristic not found " + charToRead); } else { Log.d(TAG, "readAllCharacteristics: read characteristic " + charToRead); - mBluetoothLeService.readCharacteristic(gattChar); + mBluetoothLeService.readCharacteristic(UUID.fromString(WEAZARD_SERVICE), gattChar.getUuid()); } } } public void writeAllCharacteristics() { - // TODO : move it from there... - supportedServices = mBluetoothLeService.getSupportedGattServices(); - extractCharacteristicsFromGattServices(supportedServices); - - List<String> characteristicsToRead = Arrays.asList( + /** + * Please put TX in last, it force the reconnection to restart and then you can't write the + * rest of the characteristics + */ + List<String> characteristicsToWrite = Arrays.asList( DEVICE_NAME_CHARACTERISTIC, - WEAZARD_NAMESPACE_CHARACTERISTIC, - WEAZARD_INFOS_CHARACTERISTIC, - TX_POWER_CHARACTERISTIC, + NAMESPACE_CHARACTERISTIC, FIRST_NAME_CHARACTERISTIC, - LAST_NAME_CHARACTERISTIC); - - BluetoothGattCharacteristic gattChar; - for (String charToRead : characteristicsToRead) { - if ((gattChar = findCharacteristicInCharacteristicsSupported(WEAZARD_SERVICE, charToRead)) == null) { - Log.d(TAG, "readAllCharacteristics: characteristic not found " + charToRead); - } else { - Log.d(TAG, "readAllCharacteristics: read characteristic " + charToRead); - mBluetoothLeService.readCharacteristic(gattChar); + LAST_NAME_CHARACTERISTIC, + TX_POWER_CHARACTERISTIC); + + for (String charToWrite : characteristicsToWrite) { + if (charToWrite.equals(DEVICE_NAME_CHARACTERISTIC)) { + writeCharacteristic(charToWrite, etDeviceName.getText().toString().getBytes()); + } else if (charToWrite.equals(NAMESPACE_CHARACTERISTIC)) { + byte[] namespace = new byte[1]; + namespace[0] = Byte.parseByte(etNamespace.getText().toString()); + writeCharacteristic(charToWrite, namespace); + } else if (charToWrite.equals(TX_POWER_CHARACTERISTIC)) { + byte[] namespace = new byte[1]; + namespace[0] = Byte.parseByte(spinnerTxPower.getSelectedItem().toString()); + writeCharacteristic(charToWrite, namespace); + } else if (charToWrite.equals(FIRST_NAME_CHARACTERISTIC)) { + writeCharacteristic(charToWrite, etFirstName.getText().toString().getBytes()); + } else if (charToWrite.equals(LAST_NAME_CHARACTERISTIC)) { + writeCharacteristic(charToWrite, etLastName.getText().toString().getBytes()); } } } - private void enableDiscoveryTreeNotifications() { - // TODO : move it from there... - supportedServices = mBluetoothLeService.getSupportedGattServices(); - extractCharacteristicsFromGattServices(supportedServices); + private void writeCharacteristic(String charToWrite, byte[] data) { + BluetoothGattCharacteristic gattChar = findCharacteristicInCharacteristicsSupported(UUID.fromString(WEAZARD_SERVICE), UUID.fromString(charToWrite)); + if (gattChar == null) { + Log.d(TAG, "writeAllCharacteristics: characteristic not found " + charToWrite); + } else { + Log.d(TAG, "writeAllCharacteristics: write characteristic " + charToWrite); + mBluetoothLeService.writeCharacteristic(UUID.fromString(WEAZARD_SERVICE), gattChar.getUuid(), data); + } + } - String charToRead = DISCOVERYTREE_INDICATION_TREE_DATA; + private void enableNotifications(UUID serviceUUID, UUID charUUID) { BluetoothGattCharacteristic gattChar; - if ((gattChar = findCharacteristicInCharacteristicsSupported(WEAZARD_SERVICE, charToRead)) == null) { - Log.d(TAG, "enableDisveryTreeNotifications: characteristic not found " + charToRead); + if ((gattChar = findCharacteristicInCharacteristicsSupported(serviceUUID, charUUID)) == null) { + Log.d(TAG, "enableNotifications: characteristic not found " + charUUID); } else { if ((gattChar.getProperties() | BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) { mBluetoothLeService.setCharacteristicNotification( gattChar, true); - Log.d(TAG, "enableDiscoveryTreeNotifications: characteristic " + charToRead); + Log.d(TAG, "enableNotifications: characteristic " + charUUID); } else { - Log.d(TAG, "enableDiscoveryTreeNotifications: characteristic does not support " + charToRead); + Log.d(TAG, "enableNotifications: characteristic does not support " + charUUID); } } } @@ -310,25 +341,21 @@ public class DeviceConfigurationFragment extends Fragment { } } - private BluetoothGattCharacteristic findCharacteristicInCharacteristicsSupported(String serviceUUID, String characteristicUUID) { + private BluetoothGattCharacteristic findCharacteristicInCharacteristicsSupported(UUID serviceUUID, UUID characteristicUUID) { + supportedServices = mBluetoothLeService.getSupportedGattServices(); + extractCharacteristicsFromGattServices(supportedServices); + for (ArrayList<BluetoothGattCharacteristic> listServices : mGattCharacteristics) { for (BluetoothGattCharacteristic gattChar : listServices) { - if (gattChar.getService().getUuid().equals(UUID.fromString(serviceUUID)) && - gattChar.getUuid().equals(UUID.fromString(characteristicUUID))) { - return gattChar; + if (gattChar.getService().getUuid().equals(serviceUUID)) { + UUID gattCharUUID = gattChar.getUuid(); + if (gattCharUUID.equals(characteristicUUID)) { + return gattChar; + } } } } return null; // Characteristic not found } - - private static IntentFilter makeGattUpdateIntentFilter() { - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); - intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); - return intentFilter; - } } diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/MainActivity.java b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/MainActivity.java index 27d51747f118fe19703f3abfbf12df7ba0d305a1..a84fb68f0abb906bcad5ec03e6b8315d5c7946b8 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/MainActivity.java +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/MainActivity.java @@ -50,7 +50,7 @@ import com.idevicesinc.sweetblue.utils.Interval; import java.util.ArrayList; import java.util.List; -import static com.example.android.bluetoothadvertisements.SampleGattAttributes.DISCOVERYTREE_INDICATION_TREE_DATA; +import static com.example.android.bluetoothadvertisements.SampleGattAttributes.TREE_DATA_CHARACTERISTIC; /** * Setup display fragments and ensure the device supports Bluetooth. @@ -60,38 +60,26 @@ public class MainActivity extends AppCompatActivity implements ScannerFragment.O private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1; private static final int BLE_TURN_ON_REQUEST_CODE = 2; private static final String TAG = MainActivity.class.getSimpleName(); + private final BleManagerConfig mBleManagerConfig = new BleManagerConfig() {{ + // Mostly using default options for this demo, but provide overrides here if desired. - private Toolbar toolbar; - private TabLayout tabLayout; - private ViewPager viewPager; - private ViewPagerAdapter viewPagerAdapter; + // Disabling undiscovery so the list doesn't jump around...ultimately a UI problem so should be fixed there eventually. + this.undiscoveryKeepAlive = Interval.DISABLED; + this.loggingEnabled = true; + }}; // Fragments TreeFragment treeFragment; GraphFragment graphFragment; ScannerFragment scannerFragment; DeviceConfigurationFragment deviceConfigurationFragment; - + Intent gattServiceIntent; + private Toolbar toolbar; + private TabLayout tabLayout; + private ViewPager viewPager; + private ViewPagerAdapter viewPagerAdapter; // Bluetooth service private BluetoothLeService mBluetoothLeService; - Intent gattServiceIntent; - - private MenuItem mBluetoothItem; - - // Sweetblue - private BleManager mBleManager; - private BleDevice mDevice; - - private final BleManagerConfig mBleManagerConfig = new BleManagerConfig() {{ - // Mostly using default options for this demo, but provide overrides here if desired. - - // Disabling undiscovery so the list doesn't jump around...ultimately a UI problem so should be fixed there eventually. - this.undiscoveryKeepAlive = Interval.DISABLED; - - this.loggingEnabled = true; - }}; - - // Code to manage Service lifecycle. private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override @@ -100,6 +88,7 @@ public class MainActivity extends AppCompatActivity implements ScannerFragment.O if (!mBluetoothLeService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); } + invalidateOptionsMenu(); } @Override @@ -107,7 +96,6 @@ public class MainActivity extends AppCompatActivity implements ScannerFragment.O mBluetoothLeService = null; } }; - // Handles various events fired by the Service. // ACTION_GATT_CONNECTED: connected to a GATT server. // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. @@ -138,7 +126,7 @@ public class MainActivity extends AppCompatActivity implements ScannerFragment.O String charUuid = intent.getStringExtra(BluetoothLeService.EXTRA_DATA_UUID); - if (charUuid.equals(DISCOVERYTREE_INDICATION_TREE_DATA)) { + if (charUuid.equals(TREE_DATA_CHARACTERISTIC)) { Log.d(TAG, "onReceive: data received from " + charUuid + " data: " + charData); @@ -165,6 +153,19 @@ public class MainActivity extends AppCompatActivity implements ScannerFragment.O } } }; + private MenuItem mBluetoothItem; + // Sweetblue + private BleManager mBleManager; + private BleDevice mDevice; + + private static IntentFilter makeGattUpdateIntentFilter() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); + return intentFilter; + } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { @@ -262,15 +263,6 @@ public class MainActivity extends AppCompatActivity implements ScannerFragment.O } } - private static IntentFilter makeGattUpdateIntentFilter() { - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); - intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); - return intentFilter; - } - public Intent getGattServiceIntent() { return gattServiceIntent; } @@ -280,10 +272,12 @@ public class MainActivity extends AppCompatActivity implements ScannerFragment.O getMenuInflater().inflate(R.menu.bluetooth_status_menu, menu); this.mBluetoothItem = menu.findItem(R.id.bluetooth_status); - if (mBluetoothLeService.getConnectionState() == BluetoothLeService.STATE_DISCONNECTED) { - mBluetoothItem.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_bluetooth_connected_white_24dp, null)); - } else { - mBluetoothItem.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_bluetooth_disabled_white_24dp, null)); + if (mBluetoothLeService != null) { + if (mBluetoothLeService.getConnectionState() == BluetoothLeService.STATE_DISCONNECTED) { + mBluetoothItem.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_bluetooth_connected_white_24dp, null)); + } else { + mBluetoothItem.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_bluetooth_disabled_white_24dp, null)); + } } return super.onCreateOptionsMenu(menu); @@ -413,6 +407,11 @@ public class MainActivity extends AppCompatActivity implements ScannerFragment.O mFragmentList.clear(); mFragmentTitleList.clear(); } + + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + } } diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/SampleGattAttributes.java b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/SampleGattAttributes.java index 78e6845e536d7292f378d9b29b763a0712f7cfe4..93bd62f1d011a5de2f6447d0a3bd7a03dab1ab4c 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/SampleGattAttributes.java +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/SampleGattAttributes.java @@ -29,9 +29,9 @@ public class SampleGattAttributes { public static final String WEAZARD_SERVICE = "00008eb3-0000-1000-8000-00805f9b34fb"; - public static final String WEAZARD_NAMESPACE_CHARACTERISTIC = "000045c1-0000-1000-8000-00805f9b34fb"; - public static final String DISCOVERYTREE_INDICATION_TREE_DATA = "0000a7fe-0000-1000-8000-00805f9b34fb"; - public static final String WEAZARD_INFOS_CHARACTERISTIC = "000080b8-0000-1000-8000-00805f9b34fb"; + public static final String NAMESPACE_CHARACTERISTIC = "000045c1-0000-1000-8000-00805f9b34fb"; + public static final String TREE_DATA_CHARACTERISTIC = "0000a7fe-0000-1000-8000-00805f9b34fb"; + public static final String DEVICE_INFOS_CHARACTERISTIC = "000080b8-0000-1000-8000-00805f9b34fb"; public static final String TX_POWER_CHARACTERISTIC = "00002a07-0000-1000-8000-00805f9b34fb"; public static final String DEVICE_NAME_CHARACTERISTIC = "00002a00-0000-1000-8000-00805f9b34fb"; @@ -39,9 +39,9 @@ public class SampleGattAttributes { public static final String LAST_NAME_CHARACTERISTIC = "00002a90-0000-1000-8000-00805f9b34fb"; public static List<String> minimumWeazardCharacteristicsRequired = Arrays.asList( - WEAZARD_NAMESPACE_CHARACTERISTIC, - DISCOVERYTREE_INDICATION_TREE_DATA, - WEAZARD_INFOS_CHARACTERISTIC, + NAMESPACE_CHARACTERISTIC, + TREE_DATA_CHARACTERISTIC, + DEVICE_INFOS_CHARACTERISTIC, TX_POWER_CHARACTERISTIC, DEVICE_NAME_CHARACTERISTIC, FIRST_NAME_CHARACTERISTIC, @@ -49,8 +49,8 @@ public class SampleGattAttributes { static { // Sample Characteristics. - attributes.put(WEAZARD_NAMESPACE_CHARACTERISTIC, "DiscoveryTree Namespace"); - attributes.put(DISCOVERYTREE_INDICATION_TREE_DATA, "DiscoveryTree tree data"); + attributes.put(NAMESPACE_CHARACTERISTIC, "DiscoveryTree Namespace"); + attributes.put(TREE_DATA_CHARACTERISTIC, "DiscoveryTree tree data"); // Weazard Services. attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service"); diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/ScannerFragment.java b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/ScannerFragment.java index 8761fb1f90a0f0e5b10303774f769beccca349b1..a383ce3878b93f6d366f79ea64a32940652bab2a 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/ScannerFragment.java +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/java/com/example/android/bluetoothadvertisements/ScannerFragment.java @@ -16,10 +16,8 @@ package com.example.android.bluetoothadvertisements; -import android.bluetooth.BluetoothAdapter; import android.os.Bundle; import android.os.Handler; -import android.support.annotation.NonNull; import android.support.v4.app.ListFragment; import android.util.Log; import android.view.LayoutInflater; @@ -34,10 +32,7 @@ import android.widget.Toast; import com.idevicesinc.sweetblue.BleDevice; import com.idevicesinc.sweetblue.BleManager; import com.idevicesinc.sweetblue.BleManagerConfig; -import com.idevicesinc.sweetblue.utils.Interval; -import java.util.Collection; -import java.util.Iterator; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -54,10 +49,6 @@ public class ScannerFragment extends ListFragment { */ private static final long SCAN_PERIOD = 5000; -// private BluetoothAdapter mBluetoothAdapter; -// -// private BluetoothLeScanner mBluetoothLeScanner; - private BleManager.DiscoveryListener mScanCallback; private ScanResultAdapter mAdapter; @@ -66,20 +57,9 @@ public class ScannerFragment extends ListFragment { private BleManager mBleManager; - /** - * Must be called after object creation by MainActivity. - * - * @param btAdapter the local BluetoothAdapter - */ - public void setBluetoothAdapter(BluetoothAdapter btAdapter) { - //this.mBluetoothAdapter = btAdapter; - //mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); setRetainInstance(true); // Use getActivity().getApplicationContext() instead of just getActivity() because this @@ -101,6 +81,7 @@ public class ScannerFragment extends ListFragment { final View view = super.onCreateView(inflater, container, savedInstanceState); setListAdapter(mAdapter); + setHasOptionsMenu(true); return view; } @@ -114,7 +95,6 @@ public class ScannerFragment extends ListFragment { setEmptyText(getString(R.string.empty_list)); - // Trigger refresh on app's 1st load startScanning(); @@ -125,30 +105,28 @@ public class ScannerFragment extends ListFragment { BleDevice device = (BleDevice) mAdapter.getItem(position); Log.d(TAG, "onListItemClick: " + device.getMacAddress()); - try{ + try { ((OnScannerDeviceSelected) getActivity()).onScannerDevicePicked(device); - }catch (ClassCastException cce){ + } catch (ClassCastException cce) { } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.scanner_menu, menu); + super.onCreateOptionsMenu(menu, inflater); } @Override - public void onResume() - { + public void onResume() { super.onResume(); mBleManager.onResume(); getActivity().invalidateOptionsMenu(); } @Override - public void onPause() - { + public void onPause() { super.onPause(); mBleManager.onPause(); } @@ -164,7 +142,8 @@ public class ScannerFragment extends ListFragment { switch (item.getItemId()) { case R.id.refresh: startScanning(); - return true; + //return true; + return super.onOptionsItemSelected(item); default: return super.onOptionsItemSelected(item); } @@ -192,8 +171,8 @@ public class ScannerFragment extends ListFragment { BleManagerConfig.DefaultScanFilter scanFilter = new BleManagerConfig.DefaultScanFilter(UUID.fromString(Constants.Eddystone_UUID.toString())); // Kick off a new scan. - //mBleManager.startScan(scanFilter, new SampleScanCallback()); - mBleManager.startScan(new SampleScanCallback()); + mBleManager.startScan(scanFilter, new SampleScanCallback()); + //mBleManager.startScan(new SampleScanCallback()); String toastText = getString(R.string.scan_start_toast) + " " + TimeUnit.SECONDS.convert(SCAN_PERIOD, TimeUnit.MILLISECONDS) + " " @@ -219,43 +198,20 @@ public class ScannerFragment extends ListFragment { mAdapter.notifyDataSetChanged(); } -// /** -// * Return a List of {@link ScanFilter} objects to filter by Service UUID. -// */ -// private List<ScanFilter> buildScanFilters() { -// List<ScanFilter> scanFilters = new ArrayList<>(); -// -// ScanFilter.Builder builder = new ScanFilter.Builder(); -// // Comment out the below line to see all BLE devices around you -// builder.setServiceUuid(Constants.Eddystone_UUID); -// scanFilters.add(builder.build()); -// -// return scanFilters; -// } - -// /** -// * Return a {@link ScanSettings} object set to use low power (to preserve battery life). -// */ -// private ScanSettings buildScanSettings() { -// ScanSettings.Builder builder = new ScanSettings.Builder(); -// builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER); -// return builder.build(); -// } - /** * Custom ScanCallback object - adds to adapter on success, displays error on failure. */ private class SampleScanCallback implements BleManager.DiscoveryListener { @Override public void onEvent(DiscoveryEvent e) { - if( e.was(LifeCycle.DISCOVERED) || e.was(LifeCycle.REDISCOVERED)) { + if (e.was(LifeCycle.DISCOVERED) || e.was(LifeCycle.REDISCOVERED)) { mAdapter.add(e.device()); } mAdapter.notifyDataSetChanged(); } } - public interface OnScannerDeviceSelected{ + public interface OnScannerDeviceSelected { public void onScannerDevicePicked(BleDevice device); } } diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/res/layout/fragment_device_configuration.xml b/dev/android/DiscoveryTreeMaster/Application/src/main/res/layout/fragment_device_configuration.xml index 983008c71416c6b401af9c6f990f163b94b2b49e..73294c50727ed96025addccd1e68894b7822c95f 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/res/layout/fragment_device_configuration.xml +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/res/layout/fragment_device_configuration.xml @@ -22,6 +22,12 @@ android:layout_height="wrap_content" android:text="Write configuration to device"/> + <Button + android:id="@+id/enable_device_infos_notifications_btn" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Enable device informations notifications"/> + <Button android:id="@+id/enable_tree_notifications_btn" android:layout_width="match_parent" @@ -132,6 +138,33 @@ </EditText> </LinearLayout> + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:gravity="end" + android:paddingEnd="5dp" + android:textAlignment="gravity" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_weight="1" + android:text="TX power" > + </TextView> + <Spinner + android:gravity="center" + android:textAlignment="gravity" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_weight="1" + android:id="@+id/spin_tx_power" + android:entries="@array/tx_power_array" + android:maxLength="20" + android:text="" + android:inputType="text"> + </Spinner> + </LinearLayout> + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/res/menu-v21/bluetooth_status_menu.xml b/dev/android/DiscoveryTreeMaster/Application/src/main/res/menu-v21/bluetooth_status_menu.xml index ca0c6fc8b07fa7e65e8f0a262ca7d9c3d5aec88a..9821bba7de7cf2f46738a360112887e4ddb8326d 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/res/menu-v21/bluetooth_status_menu.xml +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/res/menu-v21/bluetooth_status_menu.xml @@ -7,4 +7,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> android:orderInCategory="1" android:icon="@drawable/ic_bluetooth_connected_white_24dp" /> + <item android:id="@+id/users_configuration" + android:title="@string/users_management" + app:showAsAction="always" + android:orderInCategory="1" + android:icon="@drawable/ic_face_white_48dp" + /> </menu> \ No newline at end of file diff --git a/dev/android/DiscoveryTreeMaster/Application/src/main/res/values/strings.xml b/dev/android/DiscoveryTreeMaster/Application/src/main/res/values/strings.xml index 86e2a2da277f7457cc11e284f96036069d384cef..56a56e2d38820627a7ed8e52066c5d30b6eee508 100644 --- a/dev/android/DiscoveryTreeMaster/Application/src/main/res/values/strings.xml +++ b/dev/android/DiscoveryTreeMaster/Application/src/main/res/values/strings.xml @@ -75,19 +75,21 @@ <string name="uhoh_message_phone_restart_ok">OK</string> <string name="uhoh_message_weirdness">Something weird happened with the underlying ble stack. It will probably recover but you may have to nuke the stack or restart your phone if you keep seeing this.</string> <string name="uhoh_message_weirdness_ok">OK</string> + <string name="users_management">Users management</string> <string-array name="tx_power_array"> - <item>-21</item> - <item>-18</item> - <item>-15</item> - <item>-9</item> - <item>-6</item> - <item>-3</item> - <item>0</item> - <item>1</item> - <item>2</item> - <item>3</item> - <item>4</item> - <item>5</item> + <item>"-21"</item> + <item>"-18"</item> + <item>"-15"</item> + <item>"-9"</item> + <item>"-6"</item> + <item>"-3"</item> + <item>"-1"</item> + <item>"0"</item> + <item>"1"</item> + <item>"2"</item> + <item>"3"</item> + <item>"4"</item> + <item>"5"</item> </string-array> </resources> \ No newline at end of file