More tests and general code cleanup
This commit is contained in:
+20
-32
@@ -5,6 +5,7 @@ import java.util.Arrays;
|
||||
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
|
||||
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
|
||||
import uk.co.alt236.bluetoothlelib.util.ByteUtils;
|
||||
import uk.co.alt236.bluetoothlelib.util.IBeaconUtils;
|
||||
|
||||
/**
|
||||
* Parses the Manufactured Data field of an iBeacon
|
||||
@@ -52,6 +53,12 @@ public final class IBeaconManufacturerData {
|
||||
private final int mMinor;
|
||||
private final String mUUID;
|
||||
|
||||
/**
|
||||
* Instantiates a new iBeacon manufacturer data object.
|
||||
*
|
||||
* @param device a {@link BluetoothLeDevice}
|
||||
* @throws IllegalArgumentException if the data is not from an iBeacon.
|
||||
*/
|
||||
public IBeaconManufacturerData(final BluetoothLeDevice device) {
|
||||
this(device.getAdRecordStore().getRecord(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getData());
|
||||
}
|
||||
@@ -59,22 +66,28 @@ public final class IBeaconManufacturerData {
|
||||
/**
|
||||
* Instantiates a new iBeacon manufacturer data object.
|
||||
*
|
||||
* @param data the {@link uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord#TYPE_MANUFACTURER_SPECIFIC_DATA} data array
|
||||
* @throws IndexOutOfBoundsException if the data array is shorter than expected
|
||||
* @param manufacturerData the {@link AdRecord#TYPE_MANUFACTURER_SPECIFIC_DATA} data array
|
||||
* @throws IllegalArgumentException if the data is not from an iBeacon.
|
||||
*/
|
||||
public IBeaconManufacturerData(final byte[] data) {
|
||||
mData = data;
|
||||
public IBeaconManufacturerData(final byte[] manufacturerData) {
|
||||
mData = manufacturerData;
|
||||
|
||||
if (!IBeaconUtils.isThisAnIBeacon(manufacturerData)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Manufacturer record '"
|
||||
+ Arrays.toString(manufacturerData)
|
||||
+ "' is not from an iBeacon.");
|
||||
}
|
||||
|
||||
final byte[] intArray = Arrays.copyOfRange(mData, 0, 2);
|
||||
ByteUtils.invertArray(intArray);
|
||||
|
||||
mCompanyIdentidier = ByteUtils.getIntFrom2ByteArray(intArray);
|
||||
|
||||
mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 2, 4));
|
||||
mUUID = calculateUUIDString(Arrays.copyOfRange(mData, 4, 20));
|
||||
mUUID = IBeaconUtils.calculateUuidString(Arrays.copyOfRange(mData, 4, 20));
|
||||
mMajor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 20, 22));
|
||||
mMinor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 22, 24));
|
||||
mCalibratedTxPower = data[24];
|
||||
mCalibratedTxPower = mData[24];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,29 +138,4 @@ public final class IBeaconManufacturerData {
|
||||
public String getUUID() {
|
||||
return mUUID;
|
||||
}
|
||||
|
||||
private static String calculateUUIDString(final byte[] uuid) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < uuid.length; i++) {
|
||||
if (i == 4) {
|
||||
sb.append('-');
|
||||
}
|
||||
if (i == 6) {
|
||||
sb.append('-');
|
||||
}
|
||||
if (i == 8) {
|
||||
sb.append('-');
|
||||
}
|
||||
if (i == 10) {
|
||||
sb.append('-');
|
||||
}
|
||||
|
||||
sb.append(
|
||||
Integer.toHexString(ByteUtils.getIntFromByte(uuid[i])));
|
||||
}
|
||||
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@ import java.util.Map;
|
||||
|
||||
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
|
||||
|
||||
public class AdRecordUtils {
|
||||
public final class AdRecordUtils {
|
||||
|
||||
private AdRecordUtils(){
|
||||
// TO AVOID INSTANTIATION
|
||||
}
|
||||
|
||||
public static String getRecordDataAsString(final AdRecord nameRecord) {
|
||||
if (nameRecord == null) {
|
||||
|
||||
@@ -9,6 +9,10 @@ public class ByteUtils {
|
||||
*/
|
||||
private static final String HEXES = "0123456789ABCDEF";
|
||||
|
||||
private ByteUtils(){
|
||||
// TO AVOID INSTANTIATION
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a pretty representation of a Byte Array as a HEX String.
|
||||
* <p/>
|
||||
|
||||
@@ -13,7 +13,7 @@ public class IBeaconUtils {
|
||||
/**
|
||||
* Calculates the accuracy of an RSSI reading.
|
||||
* <p/>
|
||||
* The code was taken from {@linktourl http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing}
|
||||
* The code was taken from <a href="http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing" /a>
|
||||
*
|
||||
* @param txPower the calibrated TX power of an iBeacon
|
||||
* @param rssi the RSSI value of the iBeacon
|
||||
@@ -28,11 +28,35 @@ public class IBeaconUtils {
|
||||
if (ratio < 1.0) {
|
||||
return Math.pow(ratio, 10);
|
||||
} else {
|
||||
final double accuracy = (0.89976) * Math.pow(ratio, 7.7095) + 0.111;
|
||||
return accuracy;
|
||||
return (0.89976) * Math.pow(ratio, 7.7095) + 0.111;
|
||||
}
|
||||
}
|
||||
|
||||
public static String calculateUuidString(final byte[] uuid) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < uuid.length; i++) {
|
||||
if (i == 4) {
|
||||
sb.append('-');
|
||||
}
|
||||
if (i == 6) {
|
||||
sb.append('-');
|
||||
}
|
||||
if (i == 8) {
|
||||
sb.append('-');
|
||||
}
|
||||
if (i == 10) {
|
||||
sb.append('-');
|
||||
}
|
||||
|
||||
final int intFromByte = ByteUtils.getIntFromByte(uuid[i]);
|
||||
sb.append(Integer.toHexString(intFromByte));
|
||||
}
|
||||
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static IBeaconDistanceDescriptor getDistanceDescriptor(final double accuracy) {
|
||||
if (accuracy < DISTANCE_THRESHOLD_WTF) {
|
||||
return IBeaconDistanceDescriptor.UNKNOWN;
|
||||
@@ -49,17 +73,6 @@ public class IBeaconUtils {
|
||||
return IBeaconDistanceDescriptor.FAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ascertains whether a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} is an iBeacon;
|
||||
*
|
||||
* @param device a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} device.
|
||||
* @return true if the device is an iBeacon, false otherwise
|
||||
*/
|
||||
public static boolean isThisAnIBeacon(final BluetoothLeDevice device) {
|
||||
return isThisAnIBeacon(
|
||||
device.getAdRecordStore().getRecordDataAsString(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ascertains whether a Manufacturer Data byte array belongs to an iBeacon;
|
||||
*
|
||||
@@ -83,4 +96,14 @@ public class IBeaconUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ascertains whether a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} is an iBeacon;
|
||||
*
|
||||
* @param device a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} device.
|
||||
* @return true if the device is an iBeacon, false otherwise
|
||||
*/
|
||||
public static boolean isThisAnIBeacon(final BluetoothLeDevice device) {
|
||||
return isThisAnIBeacon(
|
||||
device.getAdRecordStore().getRecordDataAsString(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package uk.co.alt236.bluetoothlelib.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class AdRecordUtilsTest extends TestCase {
|
||||
private static final byte[] NON_IBEACON =
|
||||
{2, 1, 26, 11, -1, 76, 0, 9, 6, 3, -32, -64, -88,
|
||||
1, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
public void testParseScanRecordAsList() throws Exception {
|
||||
final List<AdRecord> adRecords = AdRecordUtils.parseScanRecordAsList(NON_IBEACON);
|
||||
assertNotNull(adRecords);
|
||||
assertEquals(2, adRecords.size());
|
||||
|
||||
int type = AdRecord.TYPE_FLAGS;
|
||||
assertEquals(type, adRecords.get(0).getType());
|
||||
assertEquals(2, adRecords.get(0).getLength());
|
||||
|
||||
type = AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA;
|
||||
assertEquals(type, adRecords.get(1).getType());
|
||||
assertEquals(11, adRecords.get(1).getLength());
|
||||
}
|
||||
|
||||
public void testParseScanRecordAsMap() throws Exception {
|
||||
final Map<Integer, AdRecord> adRecords = AdRecordUtils.parseScanRecordAsMap(NON_IBEACON);
|
||||
assertNotNull(adRecords);
|
||||
assertEquals(2, adRecords.size());
|
||||
|
||||
int type = AdRecord.TYPE_FLAGS;
|
||||
assertEquals(type, adRecords.get(type).getType());
|
||||
assertEquals(2, adRecords.get(type).getLength());
|
||||
|
||||
type = AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA;
|
||||
assertEquals(type, adRecords.get(type).getType());
|
||||
assertEquals(11, adRecords.get(type).getLength());
|
||||
}
|
||||
|
||||
public void testParseScanRecordAsSparseArray() throws Exception {
|
||||
//
|
||||
// Cannot be tested here as it relies on Android code...
|
||||
//
|
||||
// final SparseArray<AdRecord> adRecords = AdRecordUtils.parseScanRecordAsSparseArray(NON_IBEACON);
|
||||
// assertNotNull(adRecords);
|
||||
// assertEquals(2, adRecords.size());
|
||||
// assertEquals(AdRecord.TYPE_FLAGS, adRecords.get(AdRecord.TYPE_FLAGS).getType());
|
||||
// assertEquals(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA, adRecords.get(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getType());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user