diff --git a/library/src/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java b/library/src/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java index 5d25316..30c3566 100644 --- a/library/src/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java +++ b/library/src/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java @@ -1,10 +1,15 @@ package uk.co.alt236.bluetoothlelib.device; +import java.io.Serializable; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecordStore; import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; import uk.co.alt236.bluetoothlelib.util.ByteUtils; +import uk.co.alt236.bluetoothlelib.util.LimitedLinkHashMap; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.os.Bundle; @@ -20,16 +25,25 @@ import android.os.Parcelable; */ public class BluetoothLeDevice implements Parcelable{ private static final String PARCEL_EXTRA_BLUETOOTH_DEVICE = "bluetooth_device"; - private static final String PARCEL_EXTRA_DEVICE_RSSI = "device_rssi"; + private static final String PARCEL_EXTRA_CURRENT_RSSI = "current_rssi"; + private static final String PARCEL_EXTRA_CURRENT_TIMESTAMP = "current_timestamp"; + private static final String PARCEL_EXTRA_DEVICE_RSSI_LOG = "device_rssi_log"; private static final String PARCEL_EXTRA_DEVICE_SCANRECORD = "device_scanrecord"; private static final String PARCEL_EXTRA_DEVICE_SCANRECORD_STORE = "device_scanrecord_store"; - private static final String PARCEL_EXTRA_TIMESTAMP = "timestamp"; + private static final String PARCEL_EXTRA_FIRST_RSSI = "device_first_rssi"; + private static final String PARCEL_EXTRA_FIRST_TIMESTAMP = "first_timestamp"; + + private static final int MAX_RSSI_LOG_SIZE = 10; + private static final long LOG_INVALIDATION_THRESHOLD = 10 * 1000; private final AdRecordStore mRecordStore; private final BluetoothDevice mDevice; + private final Map mRssiLog; private final byte[] mScanRecord; - private final int mRssi; - private final long mTimestamp; + private final int mFirstRssi; + private final long mFirstTimestamp; + private int mCurrentRssi; + private long mCurrentTimestamp; public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothLeDevice createFromParcel(Parcel in) { @@ -40,29 +54,60 @@ public class BluetoothLeDevice implements Parcelable{ return new BluetoothLeDevice[size]; } }; - - public BluetoothLeDevice(BluetoothDevice device, int rssi, byte[] scanRecord){ - this(device, rssi, scanRecord, 0); - } public BluetoothLeDevice(BluetoothDevice device, int rssi, byte[] scanRecord, long timestamp){ mDevice = device; - mRssi = rssi; - mScanRecord = scanRecord; + mFirstRssi = rssi; + mFirstTimestamp = timestamp; mRecordStore = new AdRecordStore(AdRecordUtils.parseScanRecordAsSparseArray(scanRecord)); - mTimestamp = timestamp; + mScanRecord = scanRecord; + mRssiLog = Collections.synchronizedMap( + new LimitedLinkHashMap(MAX_RSSI_LOG_SIZE)); + updateRssiReading(timestamp, rssi); } + public double getRunningAverageRssi(){ + final Collection values = mRssiLog.values(); + int sum = 0; + + for(Integer value: values){ + sum += value.intValue(); + } + + return sum/values.size(); + } + + @SuppressWarnings("unchecked") protected BluetoothLeDevice(Parcel in) { final Bundle b = in.readBundle(getClass().getClassLoader()); + mCurrentRssi = b.getInt(PARCEL_EXTRA_CURRENT_RSSI, 0); + mCurrentTimestamp = b.getLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, 0); mDevice = b.getParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE); + mFirstRssi = b.getInt(PARCEL_EXTRA_FIRST_RSSI, 0); + mFirstTimestamp = b.getLong(PARCEL_EXTRA_FIRST_TIMESTAMP, 0); mRecordStore = b.getParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE); - mRssi = b.getInt(PARCEL_EXTRA_DEVICE_RSSI, 0); mScanRecord = b.getByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD); - mTimestamp = b.getLong(PARCEL_EXTRA_TIMESTAMP, 0); + + mRssiLog = Collections.synchronizedMap( + (Map) b.getSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG)); } + public synchronized void updateRssiReading(long timestamp, int rssiReading){ + addToRssiLog(timestamp, rssiReading); + } + + private void addToRssiLog(long timestamp, int rssiReading){ + + if(timestamp - mCurrentTimestamp > LOG_INVALIDATION_THRESHOLD){ + mRssiLog.clear(); + } + + mCurrentRssi = rssiReading; + mCurrentTimestamp = timestamp; + mRssiLog.put(timestamp, rssiReading); + } + @Override public int describeContents() { return 0; @@ -77,11 +122,29 @@ public class BluetoothLeDevice implements Parcelable{ if (getClass() != obj.getClass()) return false; BluetoothLeDevice other = (BluetoothLeDevice) obj; + if (mCurrentRssi != other.mCurrentRssi) + return false; + if (mCurrentTimestamp != other.mCurrentTimestamp) + return false; if (mDevice == null) { if (other.mDevice != null) return false; } else if (!mDevice.equals(other.mDevice)) return false; + if (mFirstRssi != other.mFirstRssi) + return false; + if (mFirstTimestamp != other.mFirstTimestamp) + return false; + if (mRecordStore == null) { + if (other.mRecordStore != null) + return false; + } else if (!mRecordStore.equals(other.mRecordStore)) + return false; + if (mRssiLog == null) { + if (other.mRssiLog != null) + return false; + } else if (!mRssiLog.equals(other.mRssiLog)) + return false; if (!Arrays.equals(mScanRecord, other.mScanRecord)) return false; return true; @@ -107,12 +170,20 @@ public class BluetoothLeDevice implements Parcelable{ return mDevice; } + public int getFirstRssi(){ + return mFirstRssi; + } + + public long getFirstTimestamp(){ + return mFirstTimestamp; + } + public String getName(){ return mDevice.getName(); } public int getRssi() { - return mRssi; + return mCurrentRssi; } public byte[] getScanRecord() { @@ -120,21 +191,27 @@ public class BluetoothLeDevice implements Parcelable{ } public long getTimestamp(){ - return mTimestamp; + return mCurrentTimestamp; } @Override public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + mCurrentRssi; + result = prime * result + (int) (mCurrentTimestamp ^ (mCurrentTimestamp >>> 32)); result = prime * result + ((mDevice == null) ? 0 : mDevice.hashCode()); + result = prime * result + mFirstRssi; + result = prime * result + (int) (mFirstTimestamp ^ (mFirstTimestamp >>> 32)); + result = prime * result + ((mRecordStore == null) ? 0 : mRecordStore.hashCode()); + result = prime * result + ((mRssiLog == null) ? 0 : mRssiLog.hashCode()); result = prime * result + Arrays.hashCode(mScanRecord); return result; } @Override public String toString() { - return "BluetoothLeDevice [mDevice=" + mDevice + ", mRssi=" + mRssi + ", mScanRecord=" + ByteUtils.byteArrayToHexString(mScanRecord) + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + getBluetoothDeviceClassName() + "]"; + return "BluetoothLeDevice [mDevice=" + mDevice + ", mRssi=" + mFirstRssi + ", mScanRecord=" + ByteUtils.byteArrayToHexString(mScanRecord) + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + getBluetoothDeviceClassName() + "]"; } @Override @@ -142,10 +219,16 @@ public class BluetoothLeDevice implements Parcelable{ final Bundle b = new Bundle(getClass().getClassLoader()); b.putByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD, mScanRecord); - b.putInt(PARCEL_EXTRA_DEVICE_RSSI, mRssi); - b.putLong(PARCEL_EXTRA_TIMESTAMP, mTimestamp); + + b.putInt(PARCEL_EXTRA_FIRST_RSSI, mFirstRssi); + b.putInt(PARCEL_EXTRA_CURRENT_RSSI, mCurrentRssi); + + b.putLong(PARCEL_EXTRA_FIRST_TIMESTAMP, mFirstTimestamp); + b.putLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, mCurrentTimestamp); + b.putParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE, mDevice); b.putParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE, mRecordStore); + b.putSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG, (Serializable) mRssiLog); parcel.writeBundle(b); } diff --git a/library/src/uk/co/alt236/bluetoothlelib/device/IBeaconDevice.java b/library/src/uk/co/alt236/bluetoothlelib/device/IBeaconDevice.java index 4717c3e..fe702ea 100644 --- a/library/src/uk/co/alt236/bluetoothlelib/device/IBeaconDevice.java +++ b/library/src/uk/co/alt236/bluetoothlelib/device/IBeaconDevice.java @@ -8,7 +8,7 @@ import android.os.Parcel; public class IBeaconDevice extends BluetoothLeDevice{ private final IBeaconManufacturerData mIBeaconData; - + public IBeaconDevice(BluetoothDevice device, int rssi, byte[] scanRecord) { super(device, rssi, scanRecord, 0); mIBeaconData = new IBeaconManufacturerData(this); @@ -22,40 +22,42 @@ public class IBeaconDevice extends BluetoothLeDevice{ public IBeaconDevice(BluetoothLeDevice device){ this(device.getDevice(), device.getRssi(), device.getScanRecord(), device.getTimestamp()); } - + private IBeaconDevice(Parcel in) { super(in); mIBeaconData = new IBeaconManufacturerData(this); } - + public double getAccuracy(){ - return IBeaconUtils.calculateAccuracy(getCalibratedTxPower(), getRssi()); + return IBeaconUtils.calculateAccuracy( + getCalibratedTxPower(), + getRunningAverageRssi()); } - + public int getCalibratedTxPower(){ return getIBeaconData().getCalibratedTxPower(); } - + public int getCompanyIdentifier(){ return getIBeaconData().getCompanyIdentifier(); } - + public IBeaconDistanceDescriptor getDistanceDescriptor(){ return IBeaconUtils.getDistanceDescriptor(getAccuracy()); } - + public IBeaconManufacturerData getIBeaconData(){ return mIBeaconData; } - + public int getMajor(){ return getIBeaconData().getMajor(); } - + public int getMinor(){ return getIBeaconData().getMinor(); } - + public String getUUID(){ return getIBeaconData().getUUID(); } diff --git a/library/src/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java b/library/src/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java new file mode 100644 index 0000000..d1d72e5 --- /dev/null +++ b/library/src/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java @@ -0,0 +1,20 @@ +package uk.co.alt236.bluetoothlelib.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class LimitedLinkHashMap extends LinkedHashMap{ + private static final long serialVersionUID = -5375660288461724925L; + + private final int mMaxSize; + public LimitedLinkHashMap(int maxSize){ + super(); + mMaxSize = maxSize; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return this.size() > mMaxSize; + } +}