More tests and general code cleanup

This commit is contained in:
Alexandros Schillings
2015-07-03 17:50:24 +01:00
parent 4fcbc7c468
commit 70169dc0b7
8 changed files with 200 additions and 47 deletions
+27
View File
@@ -0,0 +1,27 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Android Tests" type="AndroidTestRunConfigurationType" factoryName="Android Tests">
<module name="library" />
<option name="TESTING_TYPE" value="0" />
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
<option name="METHOD_NAME" value="" />
<option name="CLASS_NAME" value="" />
<option name="PACKAGE_NAME" value="" />
<option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
<option name="USE_LAST_SELECTED_DEVICE" value="true" />
<option name="PREFERRED_AVD" value="" />
<option name="USE_COMMAND_LINE" value="true" />
<option name="COMMAND_LINE" value="" />
<option name="WIPE_USER_DATA" value="false" />
<option name="DISABLE_BOOT_ANIMATION" value="false" />
<option name="NETWORK_SPEED" value="full" />
<option name="NETWORK_LATENCY" value="none" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" />
<option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" />
<option name="SELECTED_MATRIX_CONFIGURATION_ID" value="-1" />
<option name="SELECTED_CLOUD_PROJECT_ID" value="Please select a project..." />
<option name="IS_VALID_CLOUD_SELECTION" value="false" />
<option name="INVALID_CLOUD_SELECTION_ERROR" value="Matrix configuration not specified" />
<method />
</configuration>
</component>
+23
View File
@@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run JUnit Tests" type="JUnit" factoryName="JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="library" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" value="" />
<option name="PACKAGE_NAME" value="uk.co.alt236.bluetoothlelib" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="moduleWithDependencies" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>
+26
View File
@@ -0,0 +1,26 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Sample App" type="AndroidRunConfigurationType" factoryName="Android Application">
<module name="sample_app" />
<option name="ACTIVITY_CLASS" value="" />
<option name="MODE" value="default_activity" />
<option name="DEPLOY" value="true" />
<option name="ARTIFACT_NAME" value="" />
<option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
<option name="USE_LAST_SELECTED_DEVICE" value="true" />
<option name="PREFERRED_AVD" value="" />
<option name="USE_COMMAND_LINE" value="true" />
<option name="COMMAND_LINE" value="" />
<option name="WIPE_USER_DATA" value="false" />
<option name="DISABLE_BOOT_ANIMATION" value="false" />
<option name="NETWORK_SPEED" value="full" />
<option name="NETWORK_LATENCY" value="none" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" />
<option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" />
<option name="SELECTED_MATRIX_CONFIGURATION_ID" value="-1" />
<option name="SELECTED_CLOUD_PROJECT_ID" value="Please select a project..." />
<option name="IS_VALID_CLOUD_SELECTION" value="false" />
<option name="INVALID_CLOUD_SELECTION_ERROR" value="Matrix configuration not specified" />
<method />
</configuration>
</component>
@@ -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());
}
}