Full code reformat

This commit is contained in:
Alexandros Schillings
2015-07-03 14:52:14 +01:00
parent 5f55a3ea45
commit 53138d5462
42 changed files with 3637 additions and 3625 deletions
+5 -4
View File
@@ -1,18 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
package="uk.co.alt236.bluetoothlelib" package="uk.co.alt236.bluetoothlelib"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="2" android:versionCode="2"
android:versionName="0.0.2" > android:versionName="0.0.2">
<uses-sdk <uses-sdk
android:minSdkVersion="18" android:minSdkVersion="18"
android:targetSdkVersion="18" /> android:targetSdkVersion="18"/>
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" > android:theme="@style/AppTheme">
</application> </application>
</manifest> </manifest>
@@ -1,5 +1,10 @@
package uk.co.alt236.bluetoothlelib.device; package uk.co.alt236.bluetoothlelib.device;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
@@ -10,12 +15,9 @@ import uk.co.alt236.bluetoothlelib.resolvers.BluetoothClassResolver;
import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; import uk.co.alt236.bluetoothlelib.util.AdRecordUtils;
import uk.co.alt236.bluetoothlelib.util.ByteUtils; import uk.co.alt236.bluetoothlelib.util.ByteUtils;
import uk.co.alt236.bluetoothlelib.util.LimitedLinkHashMap; import uk.co.alt236.bluetoothlelib.util.LimitedLinkHashMap;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
// TODO: Auto-generated Javadoc // TODO: Auto-generated Javadoc
/** /**
* This is a wrapper around the default BluetoothDevice object * This is a wrapper around the default BluetoothDevice object
* As BluetoothDevice is final it cannot be extended, so to get it you * As BluetoothDevice is final it cannot be extended, so to get it you
@@ -23,373 +25,372 @@ import android.os.Parcelable;
* *
* @author Alexandros Schillings * @author Alexandros Schillings
*/ */
public class BluetoothLeDevice implements Parcelable{ public class BluetoothLeDevice implements Parcelable {
private static final String PARCEL_EXTRA_BLUETOOTH_DEVICE = "bluetooth_device"; /**
private static final String PARCEL_EXTRA_CURRENT_RSSI = "current_rssi"; * The Constant CREATOR.
private static final String PARCEL_EXTRA_CURRENT_TIMESTAMP = "current_timestamp"; */
private static final String PARCEL_EXTRA_DEVICE_RSSI_LOG = "device_rssi_log"; public static final Parcelable.Creator<BluetoothLeDevice> CREATOR = new Parcelable.Creator<BluetoothLeDevice>() {
private static final String PARCEL_EXTRA_DEVICE_SCANRECORD = "device_scanrecord"; public BluetoothLeDevice createFromParcel(final Parcel in) {
private static final String PARCEL_EXTRA_DEVICE_SCANRECORD_STORE = "device_scanrecord_store"; return new BluetoothLeDevice(in);
private static final String PARCEL_EXTRA_FIRST_RSSI = "device_first_rssi"; }
private static final String PARCEL_EXTRA_FIRST_TIMESTAMP = "first_timestamp";
private static final long LOG_INVALIDATION_THRESHOLD = 10 * 1000;
protected static final int MAX_RSSI_LOG_SIZE = 10;
private final AdRecordStore mRecordStore; public BluetoothLeDevice[] newArray(final int size) {
private final BluetoothDevice mDevice; return new BluetoothLeDevice[size];
private final Map<Long, Integer> mRssiLog; }
private final byte[] mScanRecord; };
private final int mFirstRssi; protected static final int MAX_RSSI_LOG_SIZE = 10;
private final long mFirstTimestamp; private static final String PARCEL_EXTRA_BLUETOOTH_DEVICE = "bluetooth_device";
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_FIRST_RSSI = "device_first_rssi";
private static final String PARCEL_EXTRA_FIRST_TIMESTAMP = "first_timestamp";
private static final long LOG_INVALIDATION_THRESHOLD = 10 * 1000;
private final AdRecordStore mRecordStore;
private final BluetoothDevice mDevice;
private final Map<Long, Integer> mRssiLog;
private final byte[] mScanRecord;
private final int mFirstRssi;
private final long mFirstTimestamp;
private int mCurrentRssi;
private long mCurrentTimestamp;
private int mCurrentRssi; /**
private long mCurrentTimestamp; * Instantiates a new Bluetooth LE device.
*
* @param device a standard android Bluetooth device
* @param rssi the RSSI value of the Bluetooth device
* @param scanRecord the scan record of the device
* @param timestamp the timestamp of the RSSI reading
*/
public BluetoothLeDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp) {
mDevice = device;
mFirstRssi = rssi;
mFirstTimestamp = timestamp;
mRecordStore = new AdRecordStore(AdRecordUtils.parseScanRecordAsSparseArray(scanRecord));
mScanRecord = scanRecord;
mRssiLog = new LimitedLinkHashMap<Long, Integer>(MAX_RSSI_LOG_SIZE);
updateRssiReading(timestamp, rssi);
}
/** The Constant CREATOR. */ /**
public static final Parcelable.Creator<BluetoothLeDevice> CREATOR = new Parcelable.Creator<BluetoothLeDevice>() { * Instantiates a new Bluetooth LE device.
public BluetoothLeDevice createFromParcel(final Parcel in) { *
return new BluetoothLeDevice(in); * @param device the device
} */
public BluetoothLeDevice(final BluetoothLeDevice device) {
mCurrentRssi = device.getRssi();
mCurrentTimestamp = device.getTimestamp();
mDevice = device.getDevice();
mFirstRssi = device.getFirstRssi();
mFirstTimestamp = device.getFirstTimestamp();
mRecordStore = new AdRecordStore(
AdRecordUtils.parseScanRecordAsSparseArray(device.getScanRecord()));
mRssiLog = device.getRssiLog();
mScanRecord = device.getScanRecord();
}
public BluetoothLeDevice[] newArray(final int size) { /**
return new BluetoothLeDevice[size]; * Instantiates a new bluetooth le device.
} *
}; * @param in the in
*/
@SuppressWarnings("unchecked")
protected BluetoothLeDevice(final Parcel in) {
final Bundle b = in.readBundle(getClass().getClassLoader());
/** mCurrentRssi = b.getInt(PARCEL_EXTRA_CURRENT_RSSI, 0);
* Instantiates a new Bluetooth LE device. mCurrentTimestamp = b.getLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, 0);
* mDevice = b.getParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE);
* @param device a standard android Bluetooth device mFirstRssi = b.getInt(PARCEL_EXTRA_FIRST_RSSI, 0);
* @param rssi the RSSI value of the Bluetooth device mFirstTimestamp = b.getLong(PARCEL_EXTRA_FIRST_TIMESTAMP, 0);
* @param scanRecord the scan record of the device mRecordStore = b.getParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE);
* @param timestamp the timestamp of the RSSI reading mRssiLog = (Map<Long, Integer>) b.getSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG);
*/ mScanRecord = b.getByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD);
public BluetoothLeDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp){ }
mDevice = device;
mFirstRssi = rssi;
mFirstTimestamp = timestamp;
mRecordStore = new AdRecordStore(AdRecordUtils.parseScanRecordAsSparseArray(scanRecord));
mScanRecord = scanRecord;
mRssiLog = new LimitedLinkHashMap<Long, Integer>(MAX_RSSI_LOG_SIZE);
updateRssiReading(timestamp, rssi);
}
/** /**
* Instantiates a new Bluetooth LE device. * Adds the to rssi log.
* *
* @param device the device * @param timestamp the timestamp
*/ * @param rssiReading the rssi reading
public BluetoothLeDevice(final BluetoothLeDevice device) { */
mCurrentRssi = device.getRssi(); private void addToRssiLog(final long timestamp, final int rssiReading) {
mCurrentTimestamp = device.getTimestamp(); synchronized (mRssiLog) {
mDevice = device.getDevice(); if (timestamp - mCurrentTimestamp > LOG_INVALIDATION_THRESHOLD) {
mFirstRssi = device.getFirstRssi(); mRssiLog.clear();
mFirstTimestamp = device.getFirstTimestamp(); }
mRecordStore = new AdRecordStore(
AdRecordUtils.parseScanRecordAsSparseArray(device.getScanRecord()));
mRssiLog = device.getRssiLog();
mScanRecord = device.getScanRecord();
}
/** mCurrentRssi = rssiReading;
* Instantiates a new bluetooth le device. mCurrentTimestamp = timestamp;
* mRssiLog.put(timestamp, rssiReading);
* @param in the in }
*/ }
@SuppressWarnings("unchecked")
protected BluetoothLeDevice(final Parcel in) {
final Bundle b = in.readBundle(getClass().getClassLoader());
mCurrentRssi = b.getInt(PARCEL_EXTRA_CURRENT_RSSI, 0); /* (non-Javadoc)
mCurrentTimestamp = b.getLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, 0); * @see android.os.Parcelable#describeContents()
mDevice = b.getParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE); */
mFirstRssi = b.getInt(PARCEL_EXTRA_FIRST_RSSI, 0); @Override
mFirstTimestamp = b.getLong(PARCEL_EXTRA_FIRST_TIMESTAMP, 0); public int describeContents() {
mRecordStore = b.getParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE); return 0;
mRssiLog = (Map<Long, Integer>) b.getSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG); }
mScanRecord = b.getByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD);
}
/** /* (non-Javadoc)
* Adds the to rssi log. * @see java.lang.Object#equals(java.lang.Object)
* */
* @param timestamp the timestamp @Override
* @param rssiReading the rssi reading public boolean equals(final Object obj) {
*/ if (this == obj)
private void addToRssiLog(final long timestamp, final int rssiReading){ return true;
synchronized (mRssiLog) { if (obj == null)
if(timestamp - mCurrentTimestamp > LOG_INVALIDATION_THRESHOLD){ return false;
mRssiLog.clear(); if (getClass() != obj.getClass())
} return false;
final 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;
}
mCurrentRssi = rssiReading; /**
mCurrentTimestamp = timestamp; * Gets the ad record store.
mRssiLog.put(timestamp, rssiReading); *
} * @return the ad record store
} */
public AdRecordStore getAdRecordStore() {
return mRecordStore;
}
/* (non-Javadoc) /**
* @see android.os.Parcelable#describeContents() * Gets the address.
*/ *
@Override * @return the address
public int describeContents() { */
return 0; public String getAddress() {
} return mDevice.getAddress();
}
/* (non-Javadoc) /**
* @see java.lang.Object#equals(java.lang.Object) * Gets the bluetooth device bond state.
*/ *
@Override * @return the bluetooth device bond state
public boolean equals(final Object obj) { */
if (this == obj) public String getBluetoothDeviceBondState() {
return true; return resolveBondingState(mDevice.getBondState());
if (obj == null) }
return false;
if (getClass() != obj.getClass())
return false;
final 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;
}
/** /**
* Gets the address. * Gets the bluetooth device class name.
* *
* @return the address * @return the bluetooth device class name
*/ */
public String getAddress(){ public String getBluetoothDeviceClassName() {
return mDevice.getAddress(); return BluetoothClassResolver.resolveDeviceClass(mDevice.getBluetoothClass().getDeviceClass());
} }
/** /**
* Gets the ad record store. * Gets the device.
* *
* @return the ad record store * @return the device
*/ */
public AdRecordStore getAdRecordStore(){ public BluetoothDevice getDevice() {
return mRecordStore; return mDevice;
} }
/** /**
* Gets the bluetooth device bond state. * Gets the first rssi.
* *
* @return the bluetooth device bond state * @return the first rssi
*/ */
public String getBluetoothDeviceBondState(){ public int getFirstRssi() {
return resolveBondingState(mDevice.getBondState()); return mFirstRssi;
} }
/** /**
* Gets the bluetooth device class name. * Gets the first timestamp.
* *
* @return the bluetooth device class name * @return the first timestamp
*/ */
public String getBluetoothDeviceClassName(){ public long getFirstTimestamp() {
return BluetoothClassResolver.resolveDeviceClass(mDevice.getBluetoothClass().getDeviceClass()); return mFirstTimestamp;
} }
/** /**
* Gets the device. * Gets the name.
* *
* @return the device * @return the name
*/ */
public BluetoothDevice getDevice() { public String getName() {
return mDevice; return mDevice.getName();
} }
/** /**
* Gets the first rssi. * Gets the rssi.
* *
* @return the first rssi * @return the rssi
*/ */
public int getFirstRssi(){ public int getRssi() {
return mFirstRssi; return mCurrentRssi;
} }
/** /**
* Gets the first timestamp. * Gets the rssi log.
* *
* @return the first timestamp * @return the rssi log
*/ */
public long getFirstTimestamp(){ protected Map<Long, Integer> getRssiLog() {
return mFirstTimestamp; synchronized (mRssiLog) {
} return mRssiLog;
}
}
/** /**
* Gets the name. * Gets the running average rssi.
* *
* @return the name * @return the running average rssi
*/ */
public String getName(){ public double getRunningAverageRssi() {
return mDevice.getName(); int sum = 0;
} int count = 0;
/** synchronized (mRssiLog) {
* Gets the rssi. final Iterator<Long> it1 = mRssiLog.keySet().iterator();
*
* @return the rssi
*/
public int getRssi() {
return mCurrentRssi;
}
/** while (it1.hasNext()) {
* Gets the rssi log. count++;
* sum += mRssiLog.get(it1.next());
* @return the rssi log }
*/ }
protected Map<Long, Integer> getRssiLog() { // for(final Map.Entry<Long,Integer> e : mRssiLog.entrySet()){
synchronized (mRssiLog) { // count ++;
return mRssiLog; // sum += e.getValue();
} // }
}
/** if (count > 0) {
* Gets the running average rssi. return sum / count;
* } else {
* @return the running average rssi return 0;
*/ }
public double getRunningAverageRssi(){
int sum = 0;
int count = 0;
synchronized (mRssiLog) { }
final Iterator<Long> it1 = mRssiLog.keySet().iterator();
while(it1.hasNext()){ /**
count ++; * Gets the scan record.
sum += mRssiLog.get(it1.next()); *
} * @return the scan record
} */
// for(final Map.Entry<Long,Integer> e : mRssiLog.entrySet()){ public byte[] getScanRecord() {
// count ++; return mScanRecord;
// sum += e.getValue(); }
// }
if(count > 0){ /**
return sum/count; * Gets the timestamp.
} else { *
return 0; * @return the timestamp
} */
public long getTimestamp() {
return mCurrentTimestamp;
}
} /* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@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;
}
/** /* (non-Javadoc)
* Gets the scan record. * @see java.lang.Object#toString()
* */
* @return the scan record @Override
*/ public String toString() {
public byte[] getScanRecord() { return "BluetoothLeDevice [mDevice=" + mDevice + ", mRssi=" + mFirstRssi + ", mScanRecord=" + ByteUtils.byteArrayToHexString(mScanRecord) + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + getBluetoothDeviceClassName() + "]";
return mScanRecord; }
}
/** /**
* Gets the timestamp. * Update rssi reading.
* *
* @return the timestamp * @param timestamp the timestamp
*/ * @param rssiReading the rssi reading
public long getTimestamp(){ */
return mCurrentTimestamp; public void updateRssiReading(final long timestamp, final int rssiReading) {
} addToRssiLog(timestamp, rssiReading);
}
/* (non-Javadoc) /* (non-Javadoc)
* @see java.lang.Object#hashCode() * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
*/ */
@Override @Override
public int hashCode() { public void writeToParcel(final Parcel parcel, final int arg1) {
final int prime = 31; final Bundle b = new Bundle(getClass().getClassLoader());
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;
}
/* (non-Javadoc) b.putByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD, mScanRecord);
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "BluetoothLeDevice [mDevice=" + mDevice + ", mRssi=" + mFirstRssi + ", mScanRecord=" + ByteUtils.byteArrayToHexString(mScanRecord) + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + getBluetoothDeviceClassName() + "]";
}
/** b.putInt(PARCEL_EXTRA_FIRST_RSSI, mFirstRssi);
* Update rssi reading. b.putInt(PARCEL_EXTRA_CURRENT_RSSI, mCurrentRssi);
*
* @param timestamp the timestamp
* @param rssiReading the rssi reading
*/
public void updateRssiReading(final long timestamp, final int rssiReading){
addToRssiLog(timestamp, rssiReading);
}
/* (non-Javadoc) b.putLong(PARCEL_EXTRA_FIRST_TIMESTAMP, mFirstTimestamp);
* @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) b.putLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, mCurrentTimestamp);
*/
@Override
public void writeToParcel(final Parcel parcel, final int arg1) {
final Bundle b = new Bundle(getClass().getClassLoader());
b.putByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD, mScanRecord); b.putParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE, mDevice);
b.putParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE, mRecordStore);
b.putSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG, (Serializable) mRssiLog);
b.putInt(PARCEL_EXTRA_FIRST_RSSI, mFirstRssi); parcel.writeBundle(b);
b.putInt(PARCEL_EXTRA_CURRENT_RSSI, mCurrentRssi); }
b.putLong(PARCEL_EXTRA_FIRST_TIMESTAMP, mFirstTimestamp); /**
b.putLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, mCurrentTimestamp); * Resolve bonding state.
*
b.putParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE, mDevice); * @param bondState the bond state
b.putParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE, mRecordStore); * @return the string
b.putSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG, (Serializable) mRssiLog); */
private static String resolveBondingState(final int bondState) {
parcel.writeBundle(b); switch (bondState) {
} case BluetoothDevice.BOND_BONDED:
return "Paired";
/** case BluetoothDevice.BOND_BONDING:
* Resolve bonding state. return "Pairing";
* case BluetoothDevice.BOND_NONE:
* @param bondState the bond state return "Unbonded";
* @return the string default:
*/ return "Unknown";
private static String resolveBondingState(final int bondState){ }
switch (bondState){ }
case BluetoothDevice.BOND_BONDED:
return "Paired";
case BluetoothDevice.BOND_BONDING:
return "Pairing";
case BluetoothDevice.BOND_NONE:
return "Unbonded";
default:
return "Unknown";
}
}
} }
@@ -1,143 +1,146 @@
package uk.co.alt236.bluetoothlelib.device; package uk.co.alt236.bluetoothlelib.device;
import uk.co.alt236.bluetoothlelib.device.mfdata.IBeaconManufacturerData;
import uk.co.alt236.bluetoothlelib.util.IBeaconUtils;
import uk.co.alt236.bluetoothlelib.util.IBeaconUtils.IBeaconDistanceDescriptor;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.os.Parcel; import android.os.Parcel;
public class IBeaconDevice extends BluetoothLeDevice{ import uk.co.alt236.bluetoothlelib.device.mfdata.IBeaconManufacturerData;
import uk.co.alt236.bluetoothlelib.util.IBeaconDistanceDescriptor;
import uk.co.alt236.bluetoothlelib.util.IBeaconUtils;
/** The m iBeacon data. */ public class IBeaconDevice extends BluetoothLeDevice {
private final IBeaconManufacturerData mIBeaconData;
/** /**
* Instantiates a new iBeacon device. * The m iBeacon data.
* */
* @param device the device private final IBeaconManufacturerData mIBeaconData;
* @param rssi the RSSI value
* @param scanRecord the scanRecord
* @throws IllegalArgumentException if the passed device is not an iBeacon
*/
public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
super(device, rssi, scanRecord, 0);
validate();
mIBeaconData = new IBeaconManufacturerData(this);
}
/** /**
* Instantiates a new iBeacon device. * Instantiates a new iBeacon device.
* *
* @param device the device * @param device the device
* @param rssi the RSSI value of the RSSI measurement * @param rssi the RSSI value
* @param scanRecord the scan record * @param scanRecord the scanRecord
* @param timestamp the timestamp of the RSSI measurement * @throws IllegalArgumentException if the passed device is not an iBeacon
* @throws IllegalArgumentException if the passed device is not an iBeacon */
*/ public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp){ super(device, rssi, scanRecord, 0);
super(device, rssi, scanRecord, timestamp); validate();
validate(); mIBeaconData = new IBeaconManufacturerData(this);
mIBeaconData = new IBeaconManufacturerData(this); }
}
/** /**
* Will try to convert a {@link BluetoothLeDevice} into an * Instantiates a new iBeacon device.
* iBeacon Device. *
* * @param device the device
* @param device the device * @param rssi the RSSI value of the RSSI measurement
* @throws IllegalArgumentException if the passed device is not an iBeacon * @param scanRecord the scan record
*/ * @param timestamp the timestamp of the RSSI measurement
public IBeaconDevice(final BluetoothLeDevice device){ * @throws IllegalArgumentException if the passed device is not an iBeacon
super(device); */
validate(); public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp) {
mIBeaconData = new IBeaconManufacturerData(this); super(device, rssi, scanRecord, timestamp);
} validate();
mIBeaconData = new IBeaconManufacturerData(this);
}
private IBeaconDevice(final Parcel in) { /**
super(in); * Will try to convert a {@link BluetoothLeDevice} into an
validate(); * iBeacon Device.
mIBeaconData = new IBeaconManufacturerData(this); *
} * @param device the device
* @throws IllegalArgumentException if the passed device is not an iBeacon
*/
public IBeaconDevice(final BluetoothLeDevice device) {
super(device);
validate();
mIBeaconData = new IBeaconManufacturerData(this);
}
/** private IBeaconDevice(final Parcel in) {
* Gets the estimated Accuracy of the reading in meters based on super(in);
* a simple running average of the last {@link #MAX_RSSI_LOG_SIZE} validate();
* samples. mIBeaconData = new IBeaconManufacturerData(this);
* }
* @return the accuracy in meters
*/
public double getAccuracy(){
return IBeaconUtils.calculateAccuracy(
getCalibratedTxPower(),
getRunningAverageRssi());
}
/** /**
* Gets the calibrated TX power of the iBeacon device as reported. * Gets the estimated Accuracy of the reading in meters based on
* * a simple running average of the last {@link #MAX_RSSI_LOG_SIZE}
* @return the calibrated TX power * samples.
*/ *
public int getCalibratedTxPower(){ * @return the accuracy in meters
return getIBeaconData().getCalibratedTxPower(); */
} public double getAccuracy() {
return IBeaconUtils.calculateAccuracy(
getCalibratedTxPower(),
getRunningAverageRssi());
}
/** /**
* Gets the iBeacon company identifier. * Gets the calibrated TX power of the iBeacon device as reported.
* *
* @return the company identifier * @return the calibrated TX power
*/ */
public int getCompanyIdentifier(){ public int getCalibratedTxPower() {
return getIBeaconData().getCompanyIdentifier(); return getIBeaconData().getCalibratedTxPower();
} }
/** /**
* Gets the estimated Distance descriptor. * Gets the iBeacon company identifier.
* *
* @return the distance descriptor * @return the company identifier
*/ */
public IBeaconDistanceDescriptor getDistanceDescriptor(){ public int getCompanyIdentifier() {
return IBeaconUtils.getDistanceDescriptor(getAccuracy()); return getIBeaconData().getCompanyIdentifier();
} }
/** /**
* Gets the iBeacon manufacturing data. * Gets the estimated Distance descriptor.
* *
* @return the iBeacon data * @return the distance descriptor
*/ */
public IBeaconManufacturerData getIBeaconData(){ public IBeaconDistanceDescriptor getDistanceDescriptor() {
return mIBeaconData; return IBeaconUtils.getDistanceDescriptor(getAccuracy());
} }
/** /**
* Gets the iBeacon Major value. * Gets the iBeacon manufacturing data.
* *
* @return the Major value * @return the iBeacon data
*/ */
public int getMajor(){ public IBeaconManufacturerData getIBeaconData() {
return getIBeaconData().getMajor(); return mIBeaconData;
} }
/** /**
* Gets the iBeacon Minor value. * Gets the iBeacon Major value.
* *
* @return the Minor value * @return the Major value
*/ */
public int getMinor(){ public int getMajor() {
return getIBeaconData().getMinor(); return getIBeaconData().getMajor();
} }
/** /**
* Gets the iBeacon UUID. * Gets the iBeacon Minor value.
* *
* @return the UUID * @return the Minor value
*/ */
public String getUUID(){ public int getMinor() {
return getIBeaconData().getUUID(); return getIBeaconData().getMinor();
} }
private void validate(){ /**
if(!IBeaconUtils.isThisAnIBeacon(this)){ * Gets the iBeacon UUID.
throw new IllegalArgumentException("Device " + getDevice() + " is not an iBeacon."); *
} * @return the UUID
} */
public String getUUID() {
return getIBeaconData().getUUID();
}
private void validate() {
if (!IBeaconUtils.isThisAnIBeacon(this)) {
throw new IllegalArgumentException("Device " + getDevice() + " is not an iBeacon.");
}
}
} }
@@ -1,243 +1,227 @@
package uk.co.alt236.bluetoothlelib.device.adrecord; package uk.co.alt236.bluetoothlelib.device.adrecord;
import java.util.Arrays;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import java.util.Arrays;
/** /**
* Created by Dave Smith * Created by Dave Smith
* Double Encore, Inc. * Double Encore, Inc.
* * <p/>
* Expanded by Alexandros Schillings * Expanded by Alexandros Schillings
*/ */
public final class AdRecord implements Parcelable{ public final class AdRecord implements Parcelable {
// 02 # Number of bytes that follow in first AD structure // 02 # Number of bytes that follow in first AD structure
// 01 # Flags AD type // 01 # Flags AD type
// 1A # Flags value 0x1A = 000011010 // 1A # Flags value 0x1A = 000011010
// bit 0 (OFF) LE Limited Discoverable Mode // bit 0 (OFF) LE Limited Discoverable Mode
// bit 1 (ON) LE General Discoverable Mode // bit 1 (ON) LE General Discoverable Mode
// bit 2 (OFF) BR/EDR Not Supported // bit 2 (OFF) BR/EDR Not Supported
// bit 3 (ON) Simultaneous LE and BR/EDR to Same Device Capable (controller) // bit 3 (ON) Simultaneous LE and BR/EDR to Same Device Capable (controller)
// bit 4 (ON) Simultaneous LE and BR/EDR to Same Device Capable (Host) // bit 4 (ON) Simultaneous LE and BR/EDR to Same Device Capable (Host)
// 1A # Number of bytes that follow in second (and last) AD structure // 1A # Number of bytes that follow in second (and last) AD structure
// FF # Manufacturer specific data AD type // FF # Manufacturer specific data AD type
// 4C 00 # Company identifier code (0x004C == Apple) // 4C 00 # Company identifier code (0x004C == Apple)
// 02 # Byte 0 of iBeacon advertisement indicator // 02 # Byte 0 of iBeacon advertisement indicator
// 15 # Byte 1 of iBeacon advertisement indicator // 15 # Byte 1 of iBeacon advertisement indicator
// e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon proximity uuid // e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon proximity uuid
// 00 00 # major // 00 00 # major
// 00 00 # minor // 00 00 # minor
// c5 # The 2's complement of the calibrated Tx Power // c5 # The 2's complement of the calibrated Tx Power
private static final String PARCEL_RECORD_DATA = "record_data"; /**
private static final String PARCEL_RECORD_TYPE = "record_type"; * General FLAGS
private static final String PARCEL_RECORD_LENGTH = "record_length"; * <p/>
* Description: Flags
* <p/>
* Information:
* Bit 0: LE Limited Discoverable Mode
* Bit 1: LE General Discoverable Mode
* Bit 2: BR/EDR Not Supported (i.e. bit 37 of LMP Extended Feature bits Page 0)
* Bit 3: Simultaneous LE and BR/EDR to Same Device Capable (Controller) (i.e. bit 49 of LMP Extended Feature bits Page 0)
* Bit 4: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1)
* Bits 5-7 Reserved
*/
public static final int TYPE_FLAGS = 0x01;
// SERVICE
public static final int TYPE_UUID16_INC = 0x02;
public static final int TYPE_UUID16 = 0x03;
public static final int TYPE_UUID32_INC = 0x04;
public static final int TYPE_UUID32 = 0x05;
public static final int TYPE_UUID128_INC = 0x06;
public static final int TYPE_UUID128 = 0x07;
// Local name
public static final int TYPE_LOCAL_NAME_SHORT = 0x08;
public static final int TYPE_LOCAL_NAME_COMPLETE = 0x09;
// TX Power Level
public static final int TYPE_TX_POWER_LEVEL = 0x0A;
// SIMPLE PAIRING OPTIONAL OOB TAGS
public static final int TYPE_DEVICE_CLASS = 0x0D;
public static final int TYPE_SIMPLE_PAIRING_HASH_C = 0x0E;
public static final int TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F;
// SECURITY MANAGER TK VALUE
public static final int TYPE_TK_VALUE = 0x10;
/* SECURITY MANAGER OOB FLAGS
*
* Description: Flag (1 octet)
*
* Information:
* Bit 0: OOB Flags Field: (0 = OOB data not present, 1 = OOB data present)
* Bit 1: LE supported (Host) (i.e. bit 65 of LMP Extended Feature bits Page 1
* Bit 2: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1)
* Bit 3: Address type (0 = Public Address, 1 = Random Address)
* Bits 4-7 Reserved
*/
public static final int TYPE_SECURITY_MANAGER_OOB_FLAGS = 0x11;
/* SLAVE CONNECTION INTERVAL RANGE
*
* Description: Slave Connection Interval Range
*
* Information:
* The first 2 octets defines the minimum value for the connection interval in the following manner:
* connInterval min = Conn_Interval_Min * 1.25 ms
* Conn_Interval_Min range: 0x0006 to 0x0C80
* Value of 0xFFFF indicates no specific minimum.
* Values outside the range are reserved. (excluding 0xFFFF)
*
* The second 2 octets defines the maximum value for the connection interval in the following manner:
* connInterval max = Conn_Interval_Max * 1.25 ms
* Conn_Interval_Max range: 0x0006 to 0x0C80
* Conn_Interval_Max shall be equal to or greater
* than the Conn_Interval_Min.
* Value of 0xFFFF indicates no specific maximum.
* Values outside the range are reserved (excluding 0xFFFF)
*/
public static final int TYPE_CONNECTION_INTERVAL_RANGE = 0x12;
// SERVICE SOLICITATION
public static final int TYPE_SERVICE_UUIDS_LIST_16BIT = 0x14;
public static final int TYPE_SERVICE_UUIDS_LIST_128BIT = 0x15;
/* SERVICE DATA
*
* Description: Service Data (2 or more octets)
* Information: The first 2 octets contain the 16 bit Service UUID followed by additional service data
*/
public static final int TYPE_SERVICE_DATA = 0x16;
/* MANUFACTURER SPECIFIC DATA
*
* Description: Manufacturer Specific Data (2 or more octets)
* Information: The first 2 octets contain the Company Identifier Code followed by additional manufacturer specific data
*/
public static final int TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
public static final Parcelable.Creator<AdRecord> CREATOR = new Parcelable.Creator<AdRecord>() {
public AdRecord createFromParcel(final Parcel in) {
return new AdRecord(in);
}
/** public AdRecord[] newArray(final int size) {
* General FLAGS return new AdRecord[size];
* }
* Description: Flags };
* private static final String PARCEL_RECORD_DATA = "record_data";
* Information: private static final String PARCEL_RECORD_TYPE = "record_type";
* Bit 0: LE Limited Discoverable Mode private static final String PARCEL_RECORD_LENGTH = "record_length";
* Bit 1: LE General Discoverable Mode /* Model Object Definition */
* Bit 2: BR/EDR Not Supported (i.e. bit 37 of LMP Extended Feature bits Page 0) private final int mLength;
* Bit 3: Simultaneous LE and BR/EDR to Same Device Capable (Controller) (i.e. bit 49 of LMP Extended Feature bits Page 0) private final int mType;
* Bit 4: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1) private final byte[] mData;
* Bits 5-7 Reserved
*/
public static final int TYPE_FLAGS = 0x01;
// SERVICE public AdRecord(final int length, final int type, final byte[] data) {
public static final int TYPE_UUID16_INC = 0x02; mLength = length;
public static final int TYPE_UUID16 = 0x03; mType = type;
public static final int TYPE_UUID32_INC = 0x04; mData = data;
public static final int TYPE_UUID32 = 0x05; }
public static final int TYPE_UUID128_INC = 0x06;
public static final int TYPE_UUID128 = 0x07;
// Local name public AdRecord(final Parcel in) {
public static final int TYPE_LOCAL_NAME_SHORT = 0x08; final Bundle b = in.readBundle(getClass().getClassLoader());
public static final int TYPE_LOCAL_NAME_COMPLETE = 0x09; mLength = b.getInt(PARCEL_RECORD_LENGTH);
mType = b.getInt(PARCEL_RECORD_TYPE);
mData = b.getByteArray(PARCEL_RECORD_DATA);
}
// TX Power Level @Override
public static final int TYPE_TX_POWER_LEVEL = 0x0A; public int describeContents() {
return 0;
}
// SIMPLE PAIRING OPTIONAL OOB TAGS public byte[] getData() {
public static final int TYPE_DEVICE_CLASS = 0x0D; return mData;
public static final int TYPE_SIMPLE_PAIRING_HASH_C = 0x0E; }
public static final int TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F;
// SECURITY MANAGER TK VALUE public String getHumanReadableType() {
public static final int TYPE_TK_VALUE = 0x10; return getHumanReadableAdType(mType);
}
public int getLength() {
return mLength;
}
/* SECURITY MANAGER OOB FLAGS public int getType() {
* return mType;
* Description: Flag (1 octet) }
*
* Information:
* Bit 0: OOB Flags Field: (0 = OOB data not present, 1 = OOB data present)
* Bit 1: LE supported (Host) (i.e. bit 65 of LMP Extended Feature bits Page 1
* Bit 2: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1)
* Bit 3: Address type (0 = Public Address, 1 = Random Address)
* Bits 4-7 Reserved
*/
public static final int TYPE_SECURITY_MANAGER_OOB_FLAGS = 0x11;
@Override
public String toString() {
return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + getHumanReadableType() + "]";
}
/* SLAVE CONNECTION INTERVAL RANGE @Override
* public void writeToParcel(final Parcel parcel, final int arg1) {
* Description: Slave Connection Interval Range final Bundle b = new Bundle(getClass().getClassLoader());
*
* Information:
* The first 2 octets defines the minimum value for the connection interval in the following manner:
* connInterval min = Conn_Interval_Min * 1.25 ms
* Conn_Interval_Min range: 0x0006 to 0x0C80
* Value of 0xFFFF indicates no specific minimum.
* Values outside the range are reserved. (excluding 0xFFFF)
*
* The second 2 octets defines the maximum value for the connection interval in the following manner:
* connInterval max = Conn_Interval_Max * 1.25 ms
* Conn_Interval_Max range: 0x0006 to 0x0C80
* Conn_Interval_Max shall be equal to or greater
* than the Conn_Interval_Min.
* Value of 0xFFFF indicates no specific maximum.
* Values outside the range are reserved (excluding 0xFFFF)
*/
public static final int TYPE_CONNECTION_INTERVAL_RANGE = 0x12;
// SERVICE SOLICITATION b.putInt(PARCEL_RECORD_LENGTH, mLength);
public static final int TYPE_SERVICE_UUIDS_LIST_16BIT = 0x14; b.putInt(PARCEL_RECORD_TYPE, mType);
public static final int TYPE_SERVICE_UUIDS_LIST_128BIT = 0x15; b.putByteArray(PARCEL_RECORD_DATA, mData);
/* SERVICE DATA parcel.writeBundle(b);
* }
* Description: Service Data (2 or more octets)
* Information: The first 2 octets contain the 16 bit Service UUID followed by additional service data
*/
public static final int TYPE_SERVICE_DATA = 0x16;
private static String getHumanReadableAdType(final int type) {
/* MANUFACTURER SPECIFIC DATA switch (type) {
* case TYPE_CONNECTION_INTERVAL_RANGE:
* Description: Manufacturer Specific Data (2 or more octets) return "Slave Connection Interval Range";
* Information: The first 2 octets contain the Company Identifier Code followed by additional manufacturer specific data case TYPE_DEVICE_CLASS:
*/ return "Class of device";
public static final int TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; case TYPE_FLAGS:
return "Flags";
/* Model Object Definition */ case TYPE_MANUFACTURER_SPECIFIC_DATA:
private final int mLength; return "Manufacturer Specific Data";
private final int mType; case TYPE_LOCAL_NAME_COMPLETE:
private final byte[] mData; return "Name (Complete)";
case TYPE_LOCAL_NAME_SHORT:
return "Name (Short)";
public static final Parcelable.Creator<AdRecord> CREATOR = new Parcelable.Creator<AdRecord>() { case TYPE_SECURITY_MANAGER_OOB_FLAGS:
public AdRecord createFromParcel(final Parcel in) { return "Security Manager OOB Flags";
return new AdRecord(in); case TYPE_SERVICE_UUIDS_LIST_128BIT:
} return "Service UUIDs (128bit)";
case TYPE_SERVICE_UUIDS_LIST_16BIT:
public AdRecord[] newArray(final int size) { return "Service UUIDs (16bit)";
return new AdRecord[size]; case TYPE_SERVICE_DATA:
} return "Service Data";
}; case TYPE_SIMPLE_PAIRING_HASH_C:
return "Simple Pairing Hash C";
public AdRecord(final int length, final int type, final byte[] data) { case TYPE_SIMPLE_PAIRING_RANDOMIZER_R:
mLength = length; return "Simple Pairing Randomizer R";
mType = type; case TYPE_TK_VALUE:
mData = data; return "TK Value";
} case TYPE_TX_POWER_LEVEL:
return "Transmission Power Level";
public AdRecord(final Parcel in) { case TYPE_UUID128:
final Bundle b = in.readBundle(getClass().getClassLoader()); return "Complete list of 128-bit UUIDs available";
mLength = b.getInt(PARCEL_RECORD_LENGTH); case TYPE_UUID128_INC:
mType = b.getInt(PARCEL_RECORD_TYPE); return "More 128-bit UUIDs available";
mData = b.getByteArray(PARCEL_RECORD_DATA); case TYPE_UUID16:
} return "Complete list of 16-bit UUIDs available";
case TYPE_UUID16_INC:
@Override return "More 16-bit UUIDs available";
public int describeContents() { case TYPE_UUID32:
return 0; return "Complete list of 32-bit UUIDs available";
} case TYPE_UUID32_INC:
return "More 32-bit UUIDs available";
public byte[] getData(){ default:
return mData; return "Unknown AdRecord Structure: " + type;
} }
}
public String getHumanReadableType(){
return getHumanReadableAdType(mType);
}
public int getLength() {
return mLength;
}
public int getType() {
return mType;
}
@Override
public String toString() {
return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + getHumanReadableType() + "]";
}
@Override
public void writeToParcel(final Parcel parcel, final int arg1) {
final Bundle b = new Bundle(getClass().getClassLoader());
b.putInt(PARCEL_RECORD_LENGTH, mLength);
b.putInt(PARCEL_RECORD_TYPE, mType);
b.putByteArray(PARCEL_RECORD_DATA, mData);
parcel.writeBundle(b);
}
private static String getHumanReadableAdType(final int type){
switch(type){
case TYPE_CONNECTION_INTERVAL_RANGE:
return "Slave Connection Interval Range";
case TYPE_DEVICE_CLASS:
return "Class of device";
case TYPE_FLAGS:
return "Flags";
case TYPE_MANUFACTURER_SPECIFIC_DATA:
return "Manufacturer Specific Data";
case TYPE_LOCAL_NAME_COMPLETE:
return "Name (Complete)";
case TYPE_LOCAL_NAME_SHORT:
return "Name (Short)";
case TYPE_SECURITY_MANAGER_OOB_FLAGS:
return "Security Manager OOB Flags";
case TYPE_SERVICE_UUIDS_LIST_128BIT:
return "Service UUIDs (128bit)";
case TYPE_SERVICE_UUIDS_LIST_16BIT:
return "Service UUIDs (16bit)";
case TYPE_SERVICE_DATA:
return "Service Data";
case TYPE_SIMPLE_PAIRING_HASH_C:
return "Simple Pairing Hash C";
case TYPE_SIMPLE_PAIRING_RANDOMIZER_R:
return "Simple Pairing Randomizer R";
case TYPE_TK_VALUE:
return "TK Value";
case TYPE_TX_POWER_LEVEL:
return "Transmission Power Level";
case TYPE_UUID128:
return "Complete list of 128-bit UUIDs available";
case TYPE_UUID128_INC:
return "More 128-bit UUIDs available";
case TYPE_UUID16:
return "Complete list of 16-bit UUIDs available";
case TYPE_UUID16_INC:
return "More 16-bit UUIDs available";
case TYPE_UUID32:
return "Complete list of 32-bit UUIDs available";
case TYPE_UUID32_INC:
return "More 32-bit UUIDs available";
default:
return "Unknown AdRecord Structure: " + type;
}
}
} }
@@ -1,158 +1,158 @@
package uk.co.alt236.bluetoothlelib.device.adrecord; package uk.co.alt236.bluetoothlelib.device.adrecord;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; import uk.co.alt236.bluetoothlelib.util.AdRecordUtils;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
/** /**
* The Class AdRecordStore. * The Class AdRecordStore.
*/ */
public class AdRecordStore implements Parcelable{ public class AdRecordStore implements Parcelable {
private final SparseArray<AdRecord> mAdRecords; public static final Parcelable.Creator<AdRecordStore> CREATOR = new Parcelable.Creator<AdRecordStore>() {
private final String mLocalNameComplete; public AdRecordStore createFromParcel(final Parcel in) {
private final String mLocalNameShort; return new AdRecordStore(in);
}
public static final Parcelable.Creator<AdRecordStore> CREATOR = new Parcelable.Creator<AdRecordStore>() { public AdRecordStore[] newArray(final int size) {
public AdRecordStore createFromParcel(final Parcel in) { return new AdRecordStore[size];
return new AdRecordStore(in); }
} };
private final SparseArray<AdRecord> mAdRecords;
private final String mLocalNameComplete;
private final String mLocalNameShort;
public AdRecordStore[] newArray(final int size) { public AdRecordStore(final Parcel in) {
return new AdRecordStore[size]; final Bundle b = in.readBundle(getClass().getClassLoader());
} mAdRecords = b.getSparseParcelableArray("records_array");
}; mLocalNameComplete = b.getString("local_name_complete");
mLocalNameShort = b.getString("local_name_short");
}
public AdRecordStore(final Parcel in) { /**
final Bundle b = in.readBundle(getClass().getClassLoader()); * Instantiates a new Bluetooth LE device Ad Record Store.
mAdRecords = b.getSparseParcelableArray("records_array"); *
mLocalNameComplete = b.getString("local_name_complete"); * @param adRecords the ad records
mLocalNameShort = b.getString("local_name_short"); */
} public AdRecordStore(final SparseArray<AdRecord> adRecords) {
mAdRecords = adRecords;
/** mLocalNameComplete = AdRecordUtils.getRecordDataAsString(
* Instantiates a new Bluetooth LE device Ad Record Store. mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_COMPLETE));
*
* @param adRecords the ad records
*/
public AdRecordStore(final SparseArray<AdRecord> adRecords){
mAdRecords = adRecords;
mLocalNameComplete = AdRecordUtils.getRecordDataAsString( mLocalNameShort = AdRecordUtils.getRecordDataAsString(
mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_COMPLETE)); mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_SHORT));
mLocalNameShort = AdRecordUtils.getRecordDataAsString( }
mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_SHORT));
} /* (non-Javadoc)
* @see android.os.Parcelable#describeContents()
*/
@Override
public int describeContents() {
return 0;
}
/* (non-Javadoc) /**
* @see android.os.Parcelable#describeContents() * Gets the short local device name.
*/ *
@Override * @return the local name complete
public int describeContents() { */
return 0; public String getLocalNameComplete() {
} return mLocalNameComplete;
}
/** /**
* Gets the short local device name. * Gets the complete local device name.
* *
* @return the local name complete * @return the local name short
*/ */
public String getLocalNameComplete() { public String getLocalNameShort() {
return mLocalNameComplete; return mLocalNameShort;
} }
/** /**
* Gets the complete local device name. * retrieves an individual record.
* *
* @return the local name short * @param record the record
*/ * @return the record
public String getLocalNameShort() { */
return mLocalNameShort; public AdRecord getRecord(final int record) {
} return mAdRecords.get(record);
}
/** /**
* retrieves an individual record. * Gets the record data as string.
* *
* @param record the record * @param record the record
* @return the record * @return the record data as string
*/ */
public AdRecord getRecord(final int record){ public String getRecordDataAsString(final int record) {
return mAdRecords.get(record); return AdRecordUtils.getRecordDataAsString(
} mAdRecords.get(record));
}
/** /**
* Gets the record data as string. * Gets the record as collection.
* *
* @param record the record * @return the records as collection
* @return the record data as string */
*/ public Collection<AdRecord> getRecordsAsCollection() {
public String getRecordDataAsString(final int record){ return Collections.unmodifiableCollection(asList(mAdRecords));
return AdRecordUtils.getRecordDataAsString( }
mAdRecords.get(record));
}
/** /**
* Gets the record as collection. * Checks if is record present.
* *
* @return the records as collection * @param record the record
*/ * @return true, if is record present
public Collection<AdRecord> getRecordsAsCollection() { */
return Collections.unmodifiableCollection(asList(mAdRecords)); public boolean isRecordPresent(final int record) {
} return mAdRecords.indexOfKey(record) >= 0;
}
/** /* (non-Javadoc)
* Checks if is record present. * @see java.lang.Object#toString()
* */
* @param record the record @Override
* @return true, if is record present public String toString() {
*/ return "AdRecordStore [mLocalNameComplete=" + mLocalNameComplete + ", mLocalNameShort=" + mLocalNameShort + "]";
public boolean isRecordPresent(final int record){ }
return mAdRecords.indexOfKey(record) >= 0;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see java.lang.Object#toString() * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
*/ */
@Override @Override
public String toString() { public void writeToParcel(final Parcel parcel, final int arg1) {
return "AdRecordStore [mLocalNameComplete=" + mLocalNameComplete + ", mLocalNameShort=" + mLocalNameShort + "]"; final Bundle b = new Bundle();
} b.putString("local_name_complete", mLocalNameComplete);
b.putString("local_name_short", mLocalNameShort);
b.putSparseParcelableArray("records_array", mAdRecords);
/* (non-Javadoc) parcel.writeBundle(b);
* @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) }
*/
@Override
public void writeToParcel(final Parcel parcel, final int arg1) {
final Bundle b = new Bundle();
b.putString("local_name_complete", mLocalNameComplete);
b.putString("local_name_short", mLocalNameShort);
b.putSparseParcelableArray("records_array", mAdRecords);
parcel.writeBundle(b); /**
} * As list.
*
* @param <C> the generic type
* @param sparseArray the sparse array
* @return the collection
*/
public static <C> Collection<C> asList(final SparseArray<C> sparseArray) {
if (sparseArray == null) return null;
/** final Collection<C> arrayList = new ArrayList<C>(sparseArray.size());
* As list. for (int i = 0; i < sparseArray.size(); i++) {
* arrayList.add(sparseArray.valueAt(i));
* @param <C> the generic type }
* @param sparseArray the sparse array
* @return the collection
*/
public static <C> Collection<C> asList(final SparseArray<C> sparseArray) {
if (sparseArray == null) return null;
final Collection<C> arrayList = new ArrayList<C>(sparseArray.size()); return arrayList;
for (int i = 0; i < sparseArray.size(); i++){ }
arrayList.add(sparseArray.valueAt(i));
}
return arrayList;
}
} }
@@ -8,9 +8,9 @@ import uk.co.alt236.bluetoothlelib.util.ByteUtils;
/** /**
* Parses the Manufactured Data field of an iBeacon * Parses the Manufactured Data field of an iBeacon
* * <p/>
* The parsing is based on the following schema: * The parsing is based on the following schema:
* * <p/>
* <p> * <p>
* 0 4C - Byte 1 (LSB) of Company identifier code * 0 4C - Byte 1 (LSB) of Company identifier code
* 1 00 - Byte 0 (MSB) of Company identifier code (0x004C == Apple) * 1 00 - Byte 0 (MSB) of Company identifier code (0x004C == Apple)
@@ -37,108 +37,115 @@ import uk.co.alt236.bluetoothlelib.util.ByteUtils;
* 22 00 - minor * 22 00 - minor
* 23 00 * 23 00
* 24 c5 - The 2's complement of the calibrated Tx Power * 24 c5 - The 2's complement of the calibrated Tx Power
* * <p/>
* </p> * </p>
* *
* @author Alexandros Schillings * @author Alexandros Schillings
*
*/ */
public final class IBeaconManufacturerData { public final class IBeaconManufacturerData {
private final byte[] mData; private final byte[] mData;
private final int mCalibratedTxPower; private final int mCalibratedTxPower;
private final int mCompanyIdentidier; private final int mCompanyIdentidier;
private final int mIBeaconAdvertisment; private final int mIBeaconAdvertisment;
private final int mMajor; private final int mMajor;
private final int mMinor; private final int mMinor;
private final String mUUID; private final String mUUID;
public IBeaconManufacturerData(final BluetoothLeDevice device){ public IBeaconManufacturerData(final BluetoothLeDevice device) {
this(device.getAdRecordStore().getRecord(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getData()); this(device.getAdRecordStore().getRecord(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getData());
} }
/** /**
* Instantiates a new iBeacon manufacturer data object. * 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
*/
public IBeaconManufacturerData(final byte[] data) {
mData = data;
* @param data the {@link uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord#TYPE_MANUFACTURER_SPECIFIC_DATA} data array mCompanyIdentidier = ByteUtils.getIntFrom2ByteArray(
* @throws IndexOutOfBoundsException if the data array is shorter than expected ByteUtils.invertArray(Arrays.copyOfRange(mData, 0, 2)));
*/
public IBeaconManufacturerData(final byte[] data){
mData = data;
mCompanyIdentidier = ByteUtils.getIntFrom2ByteArray( mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 2, 4));
ByteUtils.invertArray(Arrays.copyOfRange(mData, 0, 2))); mUUID = 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];
}
mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 2, 4)); /**
mUUID = calculateUUIDString(Arrays.copyOfRange(mData, 4, 20)); * Gets the calibrated TX power of the iBeacon device as reported.
mMajor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 20, 22)); *
mMinor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 22, 24)); * @return the calibrated TX power
mCalibratedTxPower = data[24]; */
} public int getCalibratedTxPower() {
return mCalibratedTxPower;
}
/** /**
* Gets the calibrated TX power of the iBeacon device as reported. * Gets the iBeacon company identifier.
* *
* @return the calibrated TX power * @return the company identifier
*/ */
public int getCalibratedTxPower(){ public int getCompanyIdentifier() {
return mCalibratedTxPower; return mCompanyIdentidier;
} }
/** public int getIBeaconAdvertisement() {
* Gets the iBeacon company identifier. return mIBeaconAdvertisment;
* }
* @return the company identifier
*/
public int getCompanyIdentifier(){
return mCompanyIdentidier;
}
public int getIBeaconAdvertisement(){ /**
return mIBeaconAdvertisment; * Gets the iBeacon Major value.
} *
* @return the Major value
*/
public int getMajor() {
return mMajor;
}
/** /**
* Gets the iBeacon Major value. * Gets the iBeacon Minor value.
* *
* @return the Major value * @return the Minor value
*/ */
public int getMajor(){ public int getMinor() {
return mMajor; return mMinor;
} }
/** /**
* Gets the iBeacon Minor value. * Gets the iBeacon UUID.
* *
* @return the Minor value * @return the UUID
*/ */
public int getMinor(){ public String getUUID() {
return mMinor; return mUUID;
} }
/** private static String calculateUUIDString(final byte[] uuid) {
* Gets the iBeacon UUID. final StringBuilder sb = new StringBuilder();
*
* @return the UUID
*/
public String getUUID(){
return mUUID;
}
private static String calculateUUIDString(final byte[] uuid){ for (int i = 0; i < uuid.length; i++) {
final StringBuilder sb = new StringBuilder(); if (i == 4) {
sb.append('-');
}
if (i == 6) {
sb.append('-');
}
if (i == 8) {
sb.append('-');
}
if (i == 10) {
sb.append('-');
}
for(int i = 0 ; i< uuid.length; i++){ sb.append(
if(i == 4){sb.append('-');} Integer.toHexString(ByteUtils.getIntFromByte(uuid[i])));
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(); return sb.toString();
} }
} }
@@ -4,110 +4,110 @@ import android.bluetooth.BluetoothClass;
public class BluetoothClassResolver { public class BluetoothClassResolver {
public static String resolveDeviceClass(final int btClass){ public static String resolveDeviceClass(final int btClass) {
switch (btClass){ switch (btClass) {
case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER: case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER:
return "A/V, Camcorder"; return "A/V, Camcorder";
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
return "A/V, Car Audio"; return "A/V, Car Audio";
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
return "A/V, Handsfree"; return "A/V, Handsfree";
case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
return "A/V, Headphones"; return "A/V, Headphones";
case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
return "A/V, HiFi Audio"; return "A/V, HiFi Audio";
case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
return "A/V, Loudspeaker"; return "A/V, Loudspeaker";
case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE: case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE:
return "A/V, Microphone"; return "A/V, Microphone";
case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
return "A/V, Portable Audio"; return "A/V, Portable Audio";
case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX: case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX:
return "A/V, Set Top Box"; return "A/V, Set Top Box";
case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED: case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED:
return "A/V, Uncategorized"; return "A/V, Uncategorized";
case BluetoothClass.Device.AUDIO_VIDEO_VCR: case BluetoothClass.Device.AUDIO_VIDEO_VCR:
return "A/V, VCR"; return "A/V, VCR";
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA: case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA:
return "A/V, Video Camera"; return "A/V, Video Camera";
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING: case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING:
return "A/V, Video Conferencing"; return "A/V, Video Conferencing";
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER: case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER:
return "A/V, Video Display and Loudspeaker"; return "A/V, Video Display and Loudspeaker";
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY: case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY:
return "A/V, Video Gaming Toy"; return "A/V, Video Gaming Toy";
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR: case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR:
return "A/V, Video Monitor"; return "A/V, Video Monitor";
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
return "A/V, Video Wearable Headset"; return "A/V, Video Wearable Headset";
case BluetoothClass.Device.COMPUTER_DESKTOP: case BluetoothClass.Device.COMPUTER_DESKTOP:
return "Computer, Desktop"; return "Computer, Desktop";
case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA: case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
return "Computer, Handheld PC/PDA"; return "Computer, Handheld PC/PDA";
case BluetoothClass.Device.COMPUTER_LAPTOP: case BluetoothClass.Device.COMPUTER_LAPTOP:
return "Computer, Laptop"; return "Computer, Laptop";
case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA: case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
return "Computer, Palm Size PC/PDA"; return "Computer, Palm Size PC/PDA";
case BluetoothClass.Device.COMPUTER_SERVER: case BluetoothClass.Device.COMPUTER_SERVER:
return "Computer, Server"; return "Computer, Server";
case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
return "Computer, Uncategorized"; return "Computer, Uncategorized";
case BluetoothClass.Device.COMPUTER_WEARABLE: case BluetoothClass.Device.COMPUTER_WEARABLE:
return "Computer, Wearable"; return "Computer, Wearable";
case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE: case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE:
return "Health, Blood Pressure"; return "Health, Blood Pressure";
case BluetoothClass.Device.HEALTH_DATA_DISPLAY: case BluetoothClass.Device.HEALTH_DATA_DISPLAY:
return "Health, Data Display"; return "Health, Data Display";
case BluetoothClass.Device.HEALTH_GLUCOSE: case BluetoothClass.Device.HEALTH_GLUCOSE:
return "Health, Glucose"; return "Health, Glucose";
case BluetoothClass.Device.HEALTH_PULSE_OXIMETER : case BluetoothClass.Device.HEALTH_PULSE_OXIMETER:
return "Health, Pulse Oximeter"; return "Health, Pulse Oximeter";
case BluetoothClass.Device.HEALTH_PULSE_RATE : case BluetoothClass.Device.HEALTH_PULSE_RATE:
return "Health, Pulse Rate"; return "Health, Pulse Rate";
case BluetoothClass.Device.HEALTH_THERMOMETER : case BluetoothClass.Device.HEALTH_THERMOMETER:
return "Health, Thermometer"; return "Health, Thermometer";
case BluetoothClass.Device.HEALTH_UNCATEGORIZED : case BluetoothClass.Device.HEALTH_UNCATEGORIZED:
return "Health, Uncategorized"; return "Health, Uncategorized";
case BluetoothClass.Device.HEALTH_WEIGHING: case BluetoothClass.Device.HEALTH_WEIGHING:
return "Health, Weighting"; return "Health, Weighting";
case BluetoothClass.Device.PHONE_CELLULAR: case BluetoothClass.Device.PHONE_CELLULAR:
return "Phone, Cellular"; return "Phone, Cellular";
case BluetoothClass.Device.PHONE_CORDLESS: case BluetoothClass.Device.PHONE_CORDLESS:
return "Phone, Cordless"; return "Phone, Cordless";
case BluetoothClass.Device.PHONE_ISDN: case BluetoothClass.Device.PHONE_ISDN:
return "Phone, ISDN"; return "Phone, ISDN";
case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY: case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY:
return "Phone, Modem or Gateway"; return "Phone, Modem or Gateway";
case BluetoothClass.Device.PHONE_SMART: case BluetoothClass.Device.PHONE_SMART:
return "Phone, Smart"; return "Phone, Smart";
case BluetoothClass.Device.PHONE_UNCATEGORIZED: case BluetoothClass.Device.PHONE_UNCATEGORIZED:
return "Phone, Uncategorized"; return "Phone, Uncategorized";
case BluetoothClass.Device.TOY_CONTROLLER: case BluetoothClass.Device.TOY_CONTROLLER:
return "Toy, Controller"; return "Toy, Controller";
case BluetoothClass.Device.TOY_DOLL_ACTION_FIGURE: case BluetoothClass.Device.TOY_DOLL_ACTION_FIGURE:
return "Toy, Doll/Action Figure"; return "Toy, Doll/Action Figure";
case BluetoothClass.Device.TOY_GAME: case BluetoothClass.Device.TOY_GAME:
return "Toy, Game"; return "Toy, Game";
case BluetoothClass.Device.TOY_ROBOT: case BluetoothClass.Device.TOY_ROBOT:
return "Toy, Robot"; return "Toy, Robot";
case BluetoothClass.Device.TOY_UNCATEGORIZED: case BluetoothClass.Device.TOY_UNCATEGORIZED:
return "Toy, Uncategorized"; return "Toy, Uncategorized";
case BluetoothClass.Device.TOY_VEHICLE: case BluetoothClass.Device.TOY_VEHICLE:
return "Toy, Vehicle"; return "Toy, Vehicle";
case BluetoothClass.Device.WEARABLE_GLASSES: case BluetoothClass.Device.WEARABLE_GLASSES:
return "Wearable, Glasses"; return "Wearable, Glasses";
case BluetoothClass.Device.WEARABLE_HELMET: case BluetoothClass.Device.WEARABLE_HELMET:
return "Wearable, Helmet"; return "Wearable, Helmet";
case BluetoothClass.Device.WEARABLE_JACKET: case BluetoothClass.Device.WEARABLE_JACKET:
return "Wearable, Jacket"; return "Wearable, Jacket";
case BluetoothClass.Device.WEARABLE_PAGER: case BluetoothClass.Device.WEARABLE_PAGER:
return "Wearable, Pager"; return "Wearable, Pager";
case BluetoothClass.Device.WEARABLE_UNCATEGORIZED: case BluetoothClass.Device.WEARABLE_UNCATEGORIZED:
return "Wearable, Uncategorized"; return "Wearable, Uncategorized";
case BluetoothClass.Device.WEARABLE_WRIST_WATCH: case BluetoothClass.Device.WEARABLE_WRIST_WATCH:
return "Wearable, Wrist Watch"; return "Wearable, Wrist Watch";
default: default:
return "Unknown, Unknown (class=" + btClass +")"; return "Unknown, Unknown (class=" + btClass + ")";
} }
} }
} }
@@ -5,382 +5,378 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
/** /**
*
* The UUIDS have been collected from the following sources: * The UUIDS have been collected from the following sources:
* * <p/>
* - http://developer.nokia.com/community/wiki/Bluetooth_Services_for_Windows_Phone * - http://developer.nokia.com/community/wiki/Bluetooth_Services_for_Windows_Phone
* - The Bluez project * - The Bluez project
* *
* @author Alexandros Schillings * @author Alexandros Schillings
*
*/ */
public class GattAttributeResolver { public class GattAttributeResolver {
public static final String BASE_GUID = "00000001-0000-1000-8000-00805f9b34fb"; public static final String BASE_GUID = "00000001-0000-1000-8000-00805f9b34fb";
public static final String SERVICE_DISCOVERY_PROTOCOL_SDP = "00000002-0000-1000-8000-00805f9b34fb"; public static final String SERVICE_DISCOVERY_PROTOCOL_SDP = "00000002-0000-1000-8000-00805f9b34fb";
public static final String USER_DATAGRAM_PROTOCOL_UDP = "00000003-0000-1000-8000-00805f9b34fb"; public static final String USER_DATAGRAM_PROTOCOL_UDP = "00000003-0000-1000-8000-00805f9b34fb";
public static final String RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM = "00000004-0000-1000-8000-00805f9b34fb"; public static final String RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM = "00000004-0000-1000-8000-00805f9b34fb";
public static final String TCP = "00000005-0000-1000-8000-00805f9b34fb"; public static final String TCP = "00000005-0000-1000-8000-00805f9b34fb";
public static final String TCSBIN = "00000006-0000-1000-8000-00805f9b34fb"; public static final String TCSBIN = "00000006-0000-1000-8000-00805f9b34fb";
public static final String TCSAT = "00000008-0000-1000-8000-00805f9b34fb"; public static final String TCSAT = "00000008-0000-1000-8000-00805f9b34fb";
public static final String OBJECT_EXCHANGE_PROTOCOL_OBEX = "00000009-0000-1000-8000-00805f9b34fb"; public static final String OBJECT_EXCHANGE_PROTOCOL_OBEX = "00000009-0000-1000-8000-00805f9b34fb";
public static final String IP = "0000000a-0000-1000-8000-00805f9b34fb"; public static final String IP = "0000000a-0000-1000-8000-00805f9b34fb";
public static final String FTP = "0000000c-0000-1000-8000-00805f9b34fb"; public static final String FTP = "0000000c-0000-1000-8000-00805f9b34fb";
public static final String HTTP = "0000000e-0000-1000-8000-00805f9b34fb"; public static final String HTTP = "0000000e-0000-1000-8000-00805f9b34fb";
public static final String WSP = "0000000f-0000-1000-8000-00805f9b34fb"; public static final String WSP = "0000000f-0000-1000-8000-00805f9b34fb";
public static final String BNEP_SVC = "00000010-0000-1000-8000-00805f9b34fb"; public static final String BNEP_SVC = "00000010-0000-1000-8000-00805f9b34fb";
public static final String UPNP_PROTOCOL = "00000011-0000-1000-8000-00805f9b34fb"; public static final String UPNP_PROTOCOL = "00000011-0000-1000-8000-00805f9b34fb";
public static final String HIDP = "00000012-0000-1000-8000-00805f9b34fb"; public static final String HIDP = "00000012-0000-1000-8000-00805f9b34fb";
public static final String HARDCOPY_CONTROL_CHANNEL_PROTOCOL = "00000014-0000-1000-8000-00805f9b34fb"; public static final String HARDCOPY_CONTROL_CHANNEL_PROTOCOL = "00000014-0000-1000-8000-00805f9b34fb";
public static final String HARDCOPY_DATA_CHANNEL_PROTOCOL = "00000016-0000-1000-8000-00805f9b34fb"; public static final String HARDCOPY_DATA_CHANNEL_PROTOCOL = "00000016-0000-1000-8000-00805f9b34fb";
public static final String HARDCOPY_NOTIFICATION_PROTOCOL = "00000017-0000-1000-8000-00805f9b34fb"; public static final String HARDCOPY_NOTIFICATION_PROTOCOL = "00000017-0000-1000-8000-00805f9b34fb";
public static final String VCTP_PROTOCOL = "00000019-0000-1000-8000-00805f9b34fb"; public static final String VCTP_PROTOCOL = "00000019-0000-1000-8000-00805f9b34fb";
public static final String VDTP_PROTOCOL = "0000001b-0000-1000-8000-00805f9b34fb"; public static final String VDTP_PROTOCOL = "0000001b-0000-1000-8000-00805f9b34fb";
public static final String CMPT_PROTOCOL = "0000001d-0000-1000-8000-00805f9b34fb"; public static final String CMPT_PROTOCOL = "0000001d-0000-1000-8000-00805f9b34fb";
public static final String UDI_C_PLANE_PROTOCOL = "0000001e-0000-1000-8000-00805f9b34fb"; public static final String UDI_C_PLANE_PROTOCOL = "0000001e-0000-1000-8000-00805f9b34fb";
public static final String MCAP_CONTROL_CHANNEL = "0000001f-0000-1000-8000-00805f9b34fb"; public static final String MCAP_CONTROL_CHANNEL = "0000001f-0000-1000-8000-00805f9b34fb";
public static final String MCAP_DATA_CHANNEL = "00000100-0000-1000-8000-00805f9b34fb"; public static final String MCAP_DATA_CHANNEL = "00000100-0000-1000-8000-00805f9b34fb";
public static final String L2CAP = "00001000-0000-1000-8000-00805f9b34fb"; public static final String L2CAP = "00001000-0000-1000-8000-00805f9b34fb";
public static final String SERVICE_DISCOVERY_SERVER = "00001001-0000-1000-8000-00805f9b34fb"; public static final String SERVICE_DISCOVERY_SERVER = "00001001-0000-1000-8000-00805f9b34fb";
public static final String BROWSE_GROUP_DESCRIPTOR = "00001002-0000-1000-8000-00805f9b34fb"; public static final String BROWSE_GROUP_DESCRIPTOR = "00001002-0000-1000-8000-00805f9b34fb";
public static final String PUBLIC_BROWSE_GROUP = "00001101-0000-1000-8000-00805f9b34fb"; public static final String PUBLIC_BROWSE_GROUP = "00001101-0000-1000-8000-00805f9b34fb";
public static final String SPP = "00001102-0000-1000-8000-00805f9b34fb"; public static final String SPP = "00001102-0000-1000-8000-00805f9b34fb";
public static final String LAN_ACCESS_USING_PPP = "00001103-0000-1000-8000-00805f9b34fb"; public static final String LAN_ACCESS_USING_PPP = "00001103-0000-1000-8000-00805f9b34fb";
public static final String DUN_GW = "00001104-0000-1000-8000-00805f9b34fb"; public static final String DUN_GW = "00001104-0000-1000-8000-00805f9b34fb";
public static final String OBEX_SYNC = "00001105-0000-1000-8000-00805f9b34fb"; public static final String OBEX_SYNC = "00001105-0000-1000-8000-00805f9b34fb";
public static final String OBEX_OBJECT_PUSH = "00001106-0000-1000-8000-00805f9b34fb"; public static final String OBEX_OBJECT_PUSH = "00001106-0000-1000-8000-00805f9b34fb";
public static final String OBEX_FILE_TRANSFER = "00001107-0000-1000-8000-00805f9b34fb"; public static final String OBEX_FILE_TRANSFER = "00001107-0000-1000-8000-00805f9b34fb";
public static final String IRMC_SYNC_COMMAND = "00001108-0000-1000-8000-00805f9b34fb"; public static final String IRMC_SYNC_COMMAND = "00001108-0000-1000-8000-00805f9b34fb";
public static final String HSP_HS = "00001109-0000-1000-8000-00805f9b34fb"; public static final String HSP_HS = "00001109-0000-1000-8000-00805f9b34fb";
public static final String CORDLESS_TELEPHONY = "0000110a-0000-1000-8000-00805f9b34fb"; public static final String CORDLESS_TELEPHONY = "0000110a-0000-1000-8000-00805f9b34fb";
public static final String AUDIO_SOURCE = "0000110b-0000-1000-8000-00805f9b34fb"; public static final String AUDIO_SOURCE = "0000110b-0000-1000-8000-00805f9b34fb";
public static final String AUDIO_SINK = "0000110c-0000-1000-8000-00805f9b34fb"; public static final String AUDIO_SINK = "0000110c-0000-1000-8000-00805f9b34fb";
public static final String AV_REMOTE_CONTROL_TARGET = "0000110d-0000-1000-8000-00805f9b34fb"; public static final String AV_REMOTE_CONTROL_TARGET = "0000110d-0000-1000-8000-00805f9b34fb";
public static final String ADVANCED_AUDIO = "0000110e-0000-1000-8000-00805f9b34fb"; public static final String ADVANCED_AUDIO = "0000110e-0000-1000-8000-00805f9b34fb";
public static final String AVRCP_REMOTE = "0000110f-0000-1000-8000-00805f9b34fb"; public static final String AVRCP_REMOTE = "0000110f-0000-1000-8000-00805f9b34fb";
public static final String VIDEO_CONFERENCING = "00001110-0000-1000-8000-00805f9b34fb"; public static final String VIDEO_CONFERENCING = "00001110-0000-1000-8000-00805f9b34fb";
public static final String INTERCOM = "00001111-0000-1000-8000-00805f9b34fb"; public static final String INTERCOM = "00001111-0000-1000-8000-00805f9b34fb";
public static final String FAX = "00001112-0000-1000-8000-00805f9b34fb"; public static final String FAX = "00001112-0000-1000-8000-00805f9b34fb";
public static final String HEADSET_PROFILE_HSP_AUDIO_GATEWAY = "00001113-0000-1000-8000-00805f9b34fb"; public static final String HEADSET_PROFILE_HSP_AUDIO_GATEWAY = "00001113-0000-1000-8000-00805f9b34fb";
public static final String WAP = "00001114-0000-1000-8000-00805f9b34fb"; public static final String WAP = "00001114-0000-1000-8000-00805f9b34fb";
public static final String WAP_CLIENT = "00001115-0000-1000-8000-00805f9b34fb"; public static final String WAP_CLIENT = "00001115-0000-1000-8000-00805f9b34fb";
public static final String PANU = "00001116-0000-1000-8000-00805f9b34fb"; public static final String PANU = "00001116-0000-1000-8000-00805f9b34fb";
public static final String NAP = "00001117-0000-1000-8000-00805f9b34fb"; public static final String NAP = "00001117-0000-1000-8000-00805f9b34fb";
public static final String GN = "00001118-0000-1000-8000-00805f9b34fb"; public static final String GN = "00001118-0000-1000-8000-00805f9b34fb";
public static final String DIRECT_PRINTING = "00001119-0000-1000-8000-00805f9b34fb"; public static final String DIRECT_PRINTING = "00001119-0000-1000-8000-00805f9b34fb";
public static final String REFERENCE_PRINTING = "0000111a-0000-1000-8000-00805f9b34fb"; public static final String REFERENCE_PRINTING = "0000111a-0000-1000-8000-00805f9b34fb";
public static final String IMAGING = "0000111b-0000-1000-8000-00805f9b34fb"; public static final String IMAGING = "0000111b-0000-1000-8000-00805f9b34fb";
public static final String IMAGING_RESPONDER = "0000111c-0000-1000-8000-00805f9b34fb"; public static final String IMAGING_RESPONDER = "0000111c-0000-1000-8000-00805f9b34fb";
public static final String IMAGING_AUTOMATIC_ARCHIVE = "0000111d-0000-1000-8000-00805f9b34fb"; public static final String IMAGING_AUTOMATIC_ARCHIVE = "0000111d-0000-1000-8000-00805f9b34fb";
public static final String IMAGING_REFERENCE_OBJECTS = "0000111e-0000-1000-8000-00805f9b34fb"; public static final String IMAGING_REFERENCE_OBJECTS = "0000111e-0000-1000-8000-00805f9b34fb";
public static final String HANDS_FREE_PROFILE_HFP = "0000111f-0000-1000-8000-00805f9b34fb"; public static final String HANDS_FREE_PROFILE_HFP = "0000111f-0000-1000-8000-00805f9b34fb";
public static final String HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY = "00001120-0000-1000-8000-00805f9b34fb"; public static final String HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY = "00001120-0000-1000-8000-00805f9b34fb";
public static final String DIRECT_PRINTING_REFERENCE_OBJECTS = "00001121-0000-1000-8000-00805f9b34fb"; public static final String DIRECT_PRINTING_REFERENCE_OBJECTS = "00001121-0000-1000-8000-00805f9b34fb";
public static final String REFLECTED_UI = "00001122-0000-1000-8000-00805f9b34fb"; public static final String REFLECTED_UI = "00001122-0000-1000-8000-00805f9b34fb";
public static final String BASIC_PRINTING = "00001123-0000-1000-8000-00805f9b34fb"; public static final String BASIC_PRINTING = "00001123-0000-1000-8000-00805f9b34fb";
public static final String PRINTING_STATUS = "00001124-0000-1000-8000-00805f9b34fb"; public static final String PRINTING_STATUS = "00001124-0000-1000-8000-00805f9b34fb";
public static final String HID = "00001125-0000-1000-8000-00805f9b34fb"; public static final String HID = "00001125-0000-1000-8000-00805f9b34fb";
public static final String HARDCOPY_CABLE_REPLACEMENT = "00001126-0000-1000-8000-00805f9b34fb"; public static final String HARDCOPY_CABLE_REPLACEMENT = "00001126-0000-1000-8000-00805f9b34fb";
public static final String HCR_PRINT = "00001127-0000-1000-8000-00805f9b34fb"; public static final String HCR_PRINT = "00001127-0000-1000-8000-00805f9b34fb";
public static final String HCR_SCAN = "00001128-0000-1000-8000-00805f9b34fb"; public static final String HCR_SCAN = "00001128-0000-1000-8000-00805f9b34fb";
public static final String COMMON_ISDN_ACCESS = "00001129-0000-1000-8000-00805f9b34fb"; public static final String COMMON_ISDN_ACCESS = "00001129-0000-1000-8000-00805f9b34fb";
public static final String VIDEO_CONFERENCING_GATEWAY = "0000112a-0000-1000-8000-00805f9b34fb"; public static final String VIDEO_CONFERENCING_GATEWAY = "0000112a-0000-1000-8000-00805f9b34fb";
public static final String UDIMT = "0000112b-0000-1000-8000-00805f9b34fb"; public static final String UDIMT = "0000112b-0000-1000-8000-00805f9b34fb";
public static final String UDITA = "0000112c-0000-1000-8000-00805f9b34fb"; public static final String UDITA = "0000112c-0000-1000-8000-00805f9b34fb";
public static final String AUDIO_VIDEO = "0000112d-0000-1000-8000-00805f9b34fb"; public static final String AUDIO_VIDEO = "0000112d-0000-1000-8000-00805f9b34fb";
public static final String SIM_ACCESS = "0000112e-0000-1000-8000-00805f9b34fb"; public static final String SIM_ACCESS = "0000112e-0000-1000-8000-00805f9b34fb";
public static final String OBEX_PCE = "0000112f-0000-1000-8000-00805f9b34fb"; public static final String OBEX_PCE = "0000112f-0000-1000-8000-00805f9b34fb";
public static final String OBEX_PSE = "00001130-0000-1000-8000-00805f9b34fb"; public static final String OBEX_PSE = "00001130-0000-1000-8000-00805f9b34fb";
public static final String OBEX_PBAP = "00001132-0000-1000-8000-00805f9b34fb"; public static final String OBEX_PBAP = "00001132-0000-1000-8000-00805f9b34fb";
public static final String OBEX_MAS = "00001133-0000-1000-8000-00805f9b34fb"; public static final String OBEX_MAS = "00001133-0000-1000-8000-00805f9b34fb";
public static final String OBEX_MNS = "00001134-0000-1000-8000-00805f9b34fb"; public static final String OBEX_MNS = "00001134-0000-1000-8000-00805f9b34fb";
public static final String OBEX_MAP = "00001200-0000-1000-8000-00805f9b34fb"; public static final String OBEX_MAP = "00001200-0000-1000-8000-00805f9b34fb";
public static final String PNP = "00001201-0000-1000-8000-00805f9b34fb"; public static final String PNP = "00001201-0000-1000-8000-00805f9b34fb";
public static final String GENERIC_NETWORKING = "00001202-0000-1000-8000-00805f9b34fb"; public static final String GENERIC_NETWORKING = "00001202-0000-1000-8000-00805f9b34fb";
public static final String GENERIC_FILE_TRANSFER = "00001203-0000-1000-8000-00805f9b34fb"; public static final String GENERIC_FILE_TRANSFER = "00001203-0000-1000-8000-00805f9b34fb";
public static final String GENERIC_AUDIO = "00001204-0000-1000-8000-00805f9b34fb"; public static final String GENERIC_AUDIO = "00001204-0000-1000-8000-00805f9b34fb";
public static final String GENERIC_TELEPHONY = "00001205-0000-1000-8000-00805f9b34fb"; public static final String GENERIC_TELEPHONY = "00001205-0000-1000-8000-00805f9b34fb";
public static final String UPNP = "00001206-0000-1000-8000-00805f9b34fb"; public static final String UPNP = "00001206-0000-1000-8000-00805f9b34fb";
public static final String UPNP_IP = "00001300-0000-1000-8000-00805f9b34fb"; public static final String UPNP_IP = "00001300-0000-1000-8000-00805f9b34fb";
public static final String ESDP_UPNP_IP_PAN = "00001301-0000-1000-8000-00805f9b34fb"; public static final String ESDP_UPNP_IP_PAN = "00001301-0000-1000-8000-00805f9b34fb";
public static final String ESDP_UPNP_IP_LAP = "00001302-0000-1000-8000-00805f9b34fb"; public static final String ESDP_UPNP_IP_LAP = "00001302-0000-1000-8000-00805f9b34fb";
public static final String ESDP_UPNP_L2CAP = "00001303-0000-1000-8000-00805f9b34fb"; public static final String ESDP_UPNP_L2CAP = "00001303-0000-1000-8000-00805f9b34fb";
public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE = "00001304-0000-1000-8000-00805f9b34fb"; public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE = "00001304-0000-1000-8000-00805f9b34fb";
public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SINK = "00001305-0000-1000-8000-00805f9b34fb"; public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SINK = "00001305-0000-1000-8000-00805f9b34fb";
public static final String VIDEO_DISTRIBUTION_PROFILE_VDP = "00001400-0000-1000-8000-00805f9b34fb"; public static final String VIDEO_DISTRIBUTION_PROFILE_VDP = "00001400-0000-1000-8000-00805f9b34fb";
public static final String HEALTH_DEVICE_PROFILE_HDP = "00001401-0000-1000-8000-00805f9b34fb"; public static final String HEALTH_DEVICE_PROFILE_HDP = "00001401-0000-1000-8000-00805f9b34fb";
public static final String HEALTH_DEVICE_PROFILE_HDP_SOURCE = "00001402-0000-1000-8000-00805f9b34fb"; public static final String HEALTH_DEVICE_PROFILE_HDP_SOURCE = "00001402-0000-1000-8000-00805f9b34fb";
public static final String HEALTH_DEVICE_PROFILE_HDP_SINK = "00001800-0000-1000-8000-00805f9b34fb"; public static final String HEALTH_DEVICE_PROFILE_HDP_SINK = "00001800-0000-1000-8000-00805f9b34fb";
public static final String GAP = "00001801-0000-1000-8000-00805f9b34fb"; public static final String GAP = "00001801-0000-1000-8000-00805f9b34fb";
public static final String GATT = "00001802-0000-1000-8000-00805f9b34fb"; public static final String GATT = "00001802-0000-1000-8000-00805f9b34fb";
public static final String IMMEDIATE_ALERT = "00001803-0000-1000-8000-00805f9b34fb"; public static final String IMMEDIATE_ALERT = "00001803-0000-1000-8000-00805f9b34fb";
public static final String LINK_LOSS = "00001804-0000-1000-8000-00805f9b34fb"; public static final String LINK_LOSS = "00001804-0000-1000-8000-00805f9b34fb";
public static final String TX_POWER = "00001809-0000-1000-8000-00805f9b34fb"; public static final String TX_POWER = "00001809-0000-1000-8000-00805f9b34fb";
public static final String HEALTH_THERMOMETER = "0000180a-0000-1000-8000-00805f9b34fb"; public static final String HEALTH_THERMOMETER = "0000180a-0000-1000-8000-00805f9b34fb";
public static final String DEVICE_INFORMATION = "0000180d-0000-1000-8000-00805f9b34fb"; public static final String DEVICE_INFORMATION = "0000180d-0000-1000-8000-00805f9b34fb";
public static final String HEART_RATE = "00001816-0000-1000-8000-00805f9b34fb"; public static final String HEART_RATE = "00001816-0000-1000-8000-00805f9b34fb";
public static final String CYCLING_SC = "00002902-0000-1000-8000-00805f9b34fb"; public static final String CYCLING_SC = "00002902-0000-1000-8000-00805f9b34fb";
public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002a00-0000-1000-8000-00805f9b34fb"; public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002a00-0000-1000-8000-00805f9b34fb";
public static final String DEVICE_NAME = "00002a01-0000-1000-8000-00805f9b34fb"; public static final String DEVICE_NAME = "00002a01-0000-1000-8000-00805f9b34fb";
public static final String APPEARANCE = "00002a02-0000-1000-8000-00805f9b34fb"; public static final String APPEARANCE = "00002a02-0000-1000-8000-00805f9b34fb";
public static final String PERIPHERAL_PRIVACY_FLAG = "00002a03-0000-1000-8000-00805f9b34fb"; public static final String PERIPHERAL_PRIVACY_FLAG = "00002a03-0000-1000-8000-00805f9b34fb";
public static final String RECONNECTION_ADDRESS = "00002a04-0000-1000-8000-00805f9b34fb"; public static final String RECONNECTION_ADDRESS = "00002a04-0000-1000-8000-00805f9b34fb";
public static final String PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS = "00002a05-0000-1000-8000-00805f9b34fb"; public static final String PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS = "00002a05-0000-1000-8000-00805f9b34fb";
public static final String SERVICE_CHANGED = "00002a06-0000-1000-8000-00805f9b34fb"; public static final String SERVICE_CHANGED = "00002a06-0000-1000-8000-00805f9b34fb";
public static final String ALERT_LEVEL = "00002a07-0000-1000-8000-00805f9b34fb"; public static final String ALERT_LEVEL = "00002a07-0000-1000-8000-00805f9b34fb";
public static final String TX_POWER_LEVEL = "00002a08-0000-1000-8000-00805f9b34fb"; public static final String TX_POWER_LEVEL = "00002a08-0000-1000-8000-00805f9b34fb";
public static final String DATE_TIME = "00002a09-0000-1000-8000-00805f9b34fb"; public static final String DATE_TIME = "00002a09-0000-1000-8000-00805f9b34fb";
public static final String DAY_OF_WEEK = "00002a0a-0000-1000-8000-00805f9b34fb"; public static final String DAY_OF_WEEK = "00002a0a-0000-1000-8000-00805f9b34fb";
public static final String DAY_DATE_TIME = "00002a0c-0000-1000-8000-00805f9b34fb"; public static final String DAY_DATE_TIME = "00002a0c-0000-1000-8000-00805f9b34fb";
public static final String EXACT_TIME_256 = "00002a0d-0000-1000-8000-00805f9b34fb"; public static final String EXACT_TIME_256 = "00002a0d-0000-1000-8000-00805f9b34fb";
public static final String DST_OFFSET = "00002a0e-0000-1000-8000-00805f9b34fb"; public static final String DST_OFFSET = "00002a0e-0000-1000-8000-00805f9b34fb";
public static final String TIME_ZONE = "00002a0f-0000-1000-8000-00805f9b34fb"; public static final String TIME_ZONE = "00002a0f-0000-1000-8000-00805f9b34fb";
public static final String LOCAL_TIME_INFORMATION = "00002a11-0000-1000-8000-00805f9b34fb"; public static final String LOCAL_TIME_INFORMATION = "00002a11-0000-1000-8000-00805f9b34fb";
public static final String TIME_WITH_DST = "00002a12-0000-1000-8000-00805f9b34fb"; public static final String TIME_WITH_DST = "00002a12-0000-1000-8000-00805f9b34fb";
public static final String TIME_ACCURACY = "00002a13-0000-1000-8000-00805f9b34fb"; public static final String TIME_ACCURACY = "00002a13-0000-1000-8000-00805f9b34fb";
public static final String TIME_SOURCE = "00002a14-0000-1000-8000-00805f9b34fb"; public static final String TIME_SOURCE = "00002a14-0000-1000-8000-00805f9b34fb";
public static final String REFERENCE_TIME_INFORMATION = "00002a16-0000-1000-8000-00805f9b34fb"; public static final String REFERENCE_TIME_INFORMATION = "00002a16-0000-1000-8000-00805f9b34fb";
public static final String TIME_UPDATE_CONTROL_POINT = "00002a17-0000-1000-8000-00805f9b34fb"; public static final String TIME_UPDATE_CONTROL_POINT = "00002a17-0000-1000-8000-00805f9b34fb";
public static final String TIME_UPDATE_STATE = "00002a1c-0000-1000-8000-00805f9b34fb"; public static final String TIME_UPDATE_STATE = "00002a1c-0000-1000-8000-00805f9b34fb";
public static final String TEMPERATURE_MEASUREMENT = "00002a1d-0000-1000-8000-00805f9b34fb"; public static final String TEMPERATURE_MEASUREMENT = "00002a1d-0000-1000-8000-00805f9b34fb";
public static final String TEMPERATURE_TYPE = "00002a1e-0000-1000-8000-00805f9b34fb"; public static final String TEMPERATURE_TYPE = "00002a1e-0000-1000-8000-00805f9b34fb";
public static final String INTERMEDIATE_TEMPERATURE = "00002a21-0000-1000-8000-00805f9b34fb"; public static final String INTERMEDIATE_TEMPERATURE = "00002a21-0000-1000-8000-00805f9b34fb";
public static final String MEASUREMENT_INTERVAL = "00002a23-0000-1000-8000-00805f9b34fb"; public static final String MEASUREMENT_INTERVAL = "00002a23-0000-1000-8000-00805f9b34fb";
public static final String SYSTEM_ID = "00002a24-0000-1000-8000-00805f9b34fb"; public static final String SYSTEM_ID = "00002a24-0000-1000-8000-00805f9b34fb";
public static final String MODEL_NUMBER_STRING = "00002a25-0000-1000-8000-00805f9b34fb"; public static final String MODEL_NUMBER_STRING = "00002a25-0000-1000-8000-00805f9b34fb";
public static final String SERIAL_NUMBER_STRING = "00002a26-0000-1000-8000-00805f9b34fb"; public static final String SERIAL_NUMBER_STRING = "00002a26-0000-1000-8000-00805f9b34fb";
public static final String FIRMWARE_REVISION_STRING = "00002a27-0000-1000-8000-00805f9b34fb"; public static final String FIRMWARE_REVISION_STRING = "00002a27-0000-1000-8000-00805f9b34fb";
public static final String HARDWARE_REVISION_STRING = "00002a28-0000-1000-8000-00805f9b34fb"; public static final String HARDWARE_REVISION_STRING = "00002a28-0000-1000-8000-00805f9b34fb";
public static final String SOFTWARE_REVISION_STRING = "00002a29-0000-1000-8000-00805f9b34fb"; public static final String SOFTWARE_REVISION_STRING = "00002a29-0000-1000-8000-00805f9b34fb";
public static final String MANUFACTURER_NAME_STRING = "00002a2a-0000-1000-8000-00805f9b34fb"; public static final String MANUFACTURER_NAME_STRING = "00002a2a-0000-1000-8000-00805f9b34fb";
public static final String IEEE_1107320601_REGULATORY = "00002a2b-0000-1000-8000-00805f9b34fb"; public static final String IEEE_1107320601_REGULATORY = "00002a2b-0000-1000-8000-00805f9b34fb";
public static final String CURRENT_TIME = "00002a35-0000-1000-8000-00805f9b34fb"; public static final String CURRENT_TIME = "00002a35-0000-1000-8000-00805f9b34fb";
public static final String BLOOD_PRESSURE_MEASUREMENT = "00002a36-0000-1000-8000-00805f9b34fb"; public static final String BLOOD_PRESSURE_MEASUREMENT = "00002a36-0000-1000-8000-00805f9b34fb";
public static final String INTERMEDIATE_CUFF_PRESSURE = "00002a37-0000-1000-8000-00805f9b34fb"; public static final String INTERMEDIATE_CUFF_PRESSURE = "00002a37-0000-1000-8000-00805f9b34fb";
public static final String HEART_RATE_MEASUREMENT = "00002a38-0000-1000-8000-00805f9b34fb"; public static final String HEART_RATE_MEASUREMENT = "00002a38-0000-1000-8000-00805f9b34fb";
public static final String BODY_SENSOR_LOCATION = "00002a39-0000-1000-8000-00805f9b34fb"; public static final String BODY_SENSOR_LOCATION = "00002a39-0000-1000-8000-00805f9b34fb";
public static final String HEART_RATE_CONTROL_POINT = "00002a3f-0000-1000-8000-00805f9b34fb"; public static final String HEART_RATE_CONTROL_POINT = "00002a3f-0000-1000-8000-00805f9b34fb";
public static final String ALERT_STATUS = "00002a40-0000-1000-8000-00805f9b34fb"; public static final String ALERT_STATUS = "00002a40-0000-1000-8000-00805f9b34fb";
public static final String RINGER_CONTROL_POINT = "00002a41-0000-1000-8000-00805f9b34fb"; public static final String RINGER_CONTROL_POINT = "00002a41-0000-1000-8000-00805f9b34fb";
public static final String RINGER_SETTING = "00002a42-0000-1000-8000-00805f9b34fb"; public static final String RINGER_SETTING = "00002a42-0000-1000-8000-00805f9b34fb";
public static final String ALERT_CATEGORY_ID_BIT_MASK = "00002a43-0000-1000-8000-00805f9b34fb"; public static final String ALERT_CATEGORY_ID_BIT_MASK = "00002a43-0000-1000-8000-00805f9b34fb";
public static final String ALERT_CATEGORY_ID = "00002a44-0000-1000-8000-00805f9b34fb"; public static final String ALERT_CATEGORY_ID = "00002a44-0000-1000-8000-00805f9b34fb";
public static final String ALERT_NOTIFICATION_CONTROL_POINT = "00002a45-0000-1000-8000-00805f9b34fb"; public static final String ALERT_NOTIFICATION_CONTROL_POINT = "00002a45-0000-1000-8000-00805f9b34fb";
public static final String UNREAD_ALERT_STATUS = "00002a46-0000-1000-8000-00805f9b34fb"; public static final String UNREAD_ALERT_STATUS = "00002a46-0000-1000-8000-00805f9b34fb";
public static final String NEW_ALERT = "00002a47-0000-1000-8000-00805f9b34fb"; public static final String NEW_ALERT = "00002a47-0000-1000-8000-00805f9b34fb";
public static final String SUPPORTED_NEW_ALERT_CATEGORY = "00002a48-0000-1000-8000-00805f9b34fb"; public static final String SUPPORTED_NEW_ALERT_CATEGORY = "00002a48-0000-1000-8000-00805f9b34fb";
public static final String SUPPORTED_UNREAD_ALERT_CATEGORY = "00002a49-0000-1000-8000-00805f9b34fb"; public static final String SUPPORTED_UNREAD_ALERT_CATEGORY = "00002a49-0000-1000-8000-00805f9b34fb";
public static final String BLOOD_PRESSURE_FEATURE = "00002a50-0000-1000-8000-00805f9b34fb"; public static final String BLOOD_PRESSURE_FEATURE = "00002a50-0000-1000-8000-00805f9b34fb";
public static final String PNPID = "00002a55-0000-1000-8000-00805f9b34fb"; public static final String PNPID = "00002a55-0000-1000-8000-00805f9b34fb";
public static final String SC_CONTROL_POINT = "00002a5b-0000-1000-8000-00805f9b34fb"; public static final String SC_CONTROL_POINT = "00002a5b-0000-1000-8000-00805f9b34fb";
public static final String CSC_MEASUREMENT = "00002a5c-0000-1000-8000-00805f9b34fb"; public static final String CSC_MEASUREMENT = "00002a5c-0000-1000-8000-00805f9b34fb";
public static final String CSC_FEATURE = "00002a5d-0000-1000-8000-00805f9b34fb"; public static final String CSC_FEATURE = "00002a5d-0000-1000-8000-00805f9b34fb";
public static final String SENSOR_LOCATION = "831c4071-7bc8-4a9c-a01c-15df25a4adbc"; public static final String SENSOR_LOCATION = "831c4071-7bc8-4a9c-a01c-15df25a4adbc";
public static final String ACTIVESYNC = "831c4071-7bc8-4a9c-a01c-15df25a4adbc"; public static final String ACTIVESYNC = "831c4071-7bc8-4a9c-a01c-15df25a4adbc";
public static final String ESTIMOTE_SERVICE = "b9403000-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_SERVICE = "b9403000-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_UUID = "b9403003-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_UUID = "b9403003-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_MAJOR = "b9403001-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_MAJOR = "b9403001-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_MINOR = "b9403002-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_MINOR = "b9403002-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_BATTERY = "b9403041-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_BATTERY = "b9403041-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_TEMPERATURE = "b9403021-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_TEMPERATURE = "b9403021-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_POWER = "b9403011-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_POWER = "b9403011-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_ADVERTISING_INTERVAL = "b9403012-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_ADVERTISING_INTERVAL = "b9403012-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_VERSION_SERVICE = "b9404000-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_VERSION_SERVICE = "b9404000-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_SOFTWARE_VERSION = "b9404001-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_SOFTWARE_VERSION = "b9404001-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_HARDWARE_VERSION = "b9404002-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_HARDWARE_VERSION = "b9404002-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_AUTHENTICATION_SERVICE = "b9402000-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_AUTHENTICATION_SERVICE = "b9402000-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_ADVERTISING_SEED = "b9402001-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_ADVERTISING_SEED = "b9402001-f5f8-466e-aff9-25556b57fe6d";
public static final String ESTIMOTE_ADVERTISING_VECTOR = "b9402002-f5f8-466e-aff9-25556b57fe6d"; public static final String ESTIMOTE_ADVERTISING_VECTOR = "b9402002-f5f8-466e-aff9-25556b57fe6d";
private final static Map<String, String> sGattAttributesMap = populateGattAttributesMap();
private final static Map<String, String> sGattAttributesMap = populateGattAttributesMap(); public static String getAttributeName(final String uuid, final String fallback) {
final String name = sGattAttributesMap.get(uuid.toLowerCase(Locale.US));
return name == null ? fallback : name;
}
public static String getAttributeName(final String uuid, final String fallback){ private static Map<String, String> populateGattAttributesMap() {
final String name = sGattAttributesMap.get(uuid.toLowerCase(Locale.US)); final Map<String, String> map = new HashMap<String, String>();
return name == null ? fallback : name;
}
private static Map<String, String> populateGattAttributesMap() { map.put(BASE_GUID, "Service Discovery Protocol (SDP)");
final Map<String, String> map = new HashMap<String, String>(); map.put(SERVICE_DISCOVERY_PROTOCOL_SDP, "User Datagram Protocol (UDP)");
map.put(USER_DATAGRAM_PROTOCOL_UDP, "Radio Frequency Communication Protocol (RFCOMM)");
map.put(BASE_GUID , "Service Discovery Protocol (SDP)"); map.put(RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM, "TCP");
map.put(SERVICE_DISCOVERY_PROTOCOL_SDP , "User Datagram Protocol (UDP)"); map.put(TCP, "TCSBIN");
map.put(USER_DATAGRAM_PROTOCOL_UDP , "Radio Frequency Communication Protocol (RFCOMM)"); map.put(TCSBIN, "TCSAT");
map.put(RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM , "TCP"); map.put(TCSAT, "Object Exchange Protocol (OBEX)");
map.put(TCP , "TCSBIN"); map.put(OBJECT_EXCHANGE_PROTOCOL_OBEX, "IP");
map.put(TCSBIN , "TCSAT"); map.put(IP, "FTP");
map.put(TCSAT , "Object Exchange Protocol (OBEX)"); map.put(FTP, "HTTP");
map.put(OBJECT_EXCHANGE_PROTOCOL_OBEX , "IP"); map.put(HTTP, "WSP");
map.put(IP , "FTP"); map.put(WSP, "BNEP_SVC");
map.put(FTP , "HTTP"); map.put(BNEP_SVC, "UPNP Protocol");
map.put(HTTP , "WSP"); map.put(UPNP_PROTOCOL, "HIDP");
map.put(WSP , "BNEP_SVC"); map.put(HIDP, "Hardcopy Control Channel Protocol");
map.put(BNEP_SVC , "UPNP Protocol"); map.put(HARDCOPY_CONTROL_CHANNEL_PROTOCOL, "Hardcopy Data Channel Protocol");
map.put(UPNP_PROTOCOL , "HIDP"); map.put(HARDCOPY_DATA_CHANNEL_PROTOCOL, "Hardcopy Notification Protocol");
map.put(HIDP , "Hardcopy Control Channel Protocol"); map.put(HARDCOPY_NOTIFICATION_PROTOCOL, "VCTP Protocol");
map.put(HARDCOPY_CONTROL_CHANNEL_PROTOCOL , "Hardcopy Data Channel Protocol"); map.put(VCTP_PROTOCOL, "VDTP Protocol");
map.put(HARDCOPY_DATA_CHANNEL_PROTOCOL , "Hardcopy Notification Protocol"); map.put(VDTP_PROTOCOL, "CMPT Protocol");
map.put(HARDCOPY_NOTIFICATION_PROTOCOL , "VCTP Protocol"); map.put(CMPT_PROTOCOL, "UDI C Plane Protocol");
map.put(VCTP_PROTOCOL , "VDTP Protocol"); map.put(UDI_C_PLANE_PROTOCOL, "MCAP Control Channel");
map.put(VDTP_PROTOCOL , "CMPT Protocol"); map.put(MCAP_CONTROL_CHANNEL, "MCAP Data Channel");
map.put(CMPT_PROTOCOL , "UDI C Plane Protocol"); map.put(MCAP_DATA_CHANNEL, "L2CAP");
map.put(UDI_C_PLANE_PROTOCOL , "MCAP Control Channel"); map.put(L2CAP, "Service Discovery Server");
map.put(MCAP_CONTROL_CHANNEL , "MCAP Data Channel"); map.put(SERVICE_DISCOVERY_SERVER, "Browse Group Descriptor");
map.put(MCAP_DATA_CHANNEL , "L2CAP"); map.put(BROWSE_GROUP_DESCRIPTOR, "Public Browse Group");
map.put(L2CAP , "Service Discovery Server"); map.put(PUBLIC_BROWSE_GROUP, "SPP");
map.put(SERVICE_DISCOVERY_SERVER , "Browse Group Descriptor"); map.put(SPP, "LAN Access Using PPP");
map.put(BROWSE_GROUP_DESCRIPTOR , "Public Browse Group"); map.put(LAN_ACCESS_USING_PPP, "DUN_GW");
map.put(PUBLIC_BROWSE_GROUP , "SPP"); map.put(DUN_GW, "OBEX_SYNC");
map.put(SPP , "LAN Access Using PPP"); map.put(OBEX_SYNC, "OBEX Object Push");
map.put(LAN_ACCESS_USING_PPP , "DUN_GW"); map.put(OBEX_OBJECT_PUSH, "OBEX File Transfer");
map.put(DUN_GW , "OBEX_SYNC"); map.put(OBEX_FILE_TRANSFER, "IrMC Sync Command");
map.put(OBEX_SYNC , "OBEX Object Push"); map.put(IRMC_SYNC_COMMAND, "HSP_HS");
map.put(OBEX_OBJECT_PUSH , "OBEX File Transfer"); map.put(HSP_HS, "Cordless Telephony");
map.put(OBEX_FILE_TRANSFER , "IrMC Sync Command"); map.put(CORDLESS_TELEPHONY, "Audio Source");
map.put(IRMC_SYNC_COMMAND , "HSP_HS"); map.put(AUDIO_SOURCE, "Audio Sink");
map.put(HSP_HS , "Cordless Telephony"); map.put(AUDIO_SINK, "AV Remote Control Target");
map.put(CORDLESS_TELEPHONY , "Audio Source"); map.put(AV_REMOTE_CONTROL_TARGET, "ADVANCED_AUDIO");
map.put(AUDIO_SOURCE , "Audio Sink"); map.put(ADVANCED_AUDIO, "AVRCP_REMOTE");
map.put(AUDIO_SINK , "AV Remote Control Target"); map.put(AVRCP_REMOTE, "Video Conferencing");
map.put(AV_REMOTE_CONTROL_TARGET , "ADVANCED_AUDIO"); map.put(VIDEO_CONFERENCING, "Intercom");
map.put(ADVANCED_AUDIO , "AVRCP_REMOTE"); map.put(INTERCOM, "FAX");
map.put(AVRCP_REMOTE , "Video Conferencing"); map.put(FAX, "Headset Profile (HSP) - Audio Gateway");
map.put(VIDEO_CONFERENCING , "Intercom"); map.put(HEADSET_PROFILE_HSP_AUDIO_GATEWAY, "WAP");
map.put(INTERCOM , "FAX"); map.put(WAP, "WAP Client");
map.put(FAX , "Headset Profile (HSP) - Audio Gateway"); map.put(WAP_CLIENT, "PANU");
map.put(HEADSET_PROFILE_HSP_AUDIO_GATEWAY , "WAP"); map.put(PANU, "NAP");
map.put(WAP , "WAP Client"); map.put(NAP, "GN");
map.put(WAP_CLIENT , "PANU"); map.put(GN, "Direct Printing");
map.put(PANU , "NAP"); map.put(DIRECT_PRINTING, "Reference Printing");
map.put(NAP , "GN"); map.put(REFERENCE_PRINTING, "Imaging");
map.put(GN , "Direct Printing"); map.put(IMAGING, "Imaging Responder");
map.put(DIRECT_PRINTING , "Reference Printing"); map.put(IMAGING_RESPONDER, "Imaging Automatic Archive");
map.put(REFERENCE_PRINTING , "Imaging"); map.put(IMAGING_AUTOMATIC_ARCHIVE, "Imaging Reference Objects");
map.put(IMAGING , "Imaging Responder"); map.put(IMAGING_REFERENCE_OBJECTS, "Hands Free Profile (HFP)");
map.put(IMAGING_RESPONDER , "Imaging Automatic Archive"); map.put(HANDS_FREE_PROFILE_HFP, "Hands Free Profile (HFP) – Audio Gateway");
map.put(IMAGING_AUTOMATIC_ARCHIVE , "Imaging Reference Objects"); map.put(HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY, "Direct Printing Reference Objects");
map.put(IMAGING_REFERENCE_OBJECTS , "Hands Free Profile (HFP)"); map.put(DIRECT_PRINTING_REFERENCE_OBJECTS, "Reflected UI");
map.put(HANDS_FREE_PROFILE_HFP , "Hands Free Profile (HFP) – Audio Gateway"); map.put(REFLECTED_UI, "Basic Printing");
map.put(HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY , "Direct Printing Reference Objects"); map.put(BASIC_PRINTING, "Printing Status");
map.put(DIRECT_PRINTING_REFERENCE_OBJECTS , "Reflected UI"); map.put(PRINTING_STATUS, "HID");
map.put(REFLECTED_UI , "Basic Printing"); map.put(HID, "Hardcopy Cable Replacement");
map.put(BASIC_PRINTING , "Printing Status"); map.put(HARDCOPY_CABLE_REPLACEMENT, "HCR Print");
map.put(PRINTING_STATUS , "HID"); map.put(HCR_PRINT, "HCR Scan");
map.put(HID , "Hardcopy Cable Replacement"); map.put(HCR_SCAN, "Common ISDN Access");
map.put(HARDCOPY_CABLE_REPLACEMENT , "HCR Print"); map.put(COMMON_ISDN_ACCESS, "Video Conferencing Gateway");
map.put(HCR_PRINT , "HCR Scan"); map.put(VIDEO_CONFERENCING_GATEWAY, "UDIMT");
map.put(HCR_SCAN , "Common ISDN Access"); map.put(UDIMT, "UDITA");
map.put(COMMON_ISDN_ACCESS , "Video Conferencing Gateway"); map.put(UDITA, "Audio Video");
map.put(VIDEO_CONFERENCING_GATEWAY , "UDIMT"); map.put(AUDIO_VIDEO, "SIM Access");
map.put(UDIMT , "UDITA"); map.put(SIM_ACCESS, "OBEX PCE");
map.put(UDITA , "Audio Video"); map.put(OBEX_PCE, "OBEX PSE");
map.put(AUDIO_VIDEO , "SIM Access"); map.put(OBEX_PSE, "OBEX PBAP");
map.put(SIM_ACCESS , "OBEX PCE"); map.put(OBEX_PBAP, "OBEX MAS");
map.put(OBEX_PCE , "OBEX PSE"); map.put(OBEX_MAS, "OBEX MNS");
map.put(OBEX_PSE , "OBEX PBAP"); map.put(OBEX_MNS, "OBEX MAP");
map.put(OBEX_PBAP , "OBEX MAS"); map.put(OBEX_MAP, "PNP");
map.put(OBEX_MAS , "OBEX MNS"); map.put(PNP, "Generic Networking");
map.put(OBEX_MNS , "OBEX MAP"); map.put(GENERIC_NETWORKING, "Generic File Transfer");
map.put(OBEX_MAP , "PNP"); map.put(GENERIC_FILE_TRANSFER, "Generic Audio");
map.put(PNP , "Generic Networking"); map.put(GENERIC_AUDIO, "Generic Telephony");
map.put(GENERIC_NETWORKING , "Generic File Transfer"); map.put(GENERIC_TELEPHONY, "UPNP");
map.put(GENERIC_FILE_TRANSFER , "Generic Audio"); map.put(UPNP, "UPNP IP");
map.put(GENERIC_AUDIO , "Generic Telephony"); map.put(UPNP_IP, "ESDP UPnP IP PAN");
map.put(GENERIC_TELEPHONY , "UPNP"); map.put(ESDP_UPNP_IP_PAN, "ESDP UPnP IP LAP");
map.put(UPNP , "UPNP IP"); map.put(ESDP_UPNP_IP_LAP, "ESDP Upnp L2CAP");
map.put(UPNP_IP , "ESDP UPnP IP PAN"); map.put(ESDP_UPNP_L2CAP, "Video Distribution Profile (VDP) - Source");
map.put(ESDP_UPNP_IP_PAN , "ESDP UPnP IP LAP"); map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE, "Video Distribution Profile (VDP) - Sink");
map.put(ESDP_UPNP_IP_LAP , "ESDP Upnp L2CAP"); map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SINK, "Video Distribution Profile (VDP)");
map.put(ESDP_UPNP_L2CAP , "Video Distribution Profile (VDP) - Source"); map.put(VIDEO_DISTRIBUTION_PROFILE_VDP, "Health Device Profile (HDP)");
map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE , "Video Distribution Profile (VDP) - Sink"); map.put(HEALTH_DEVICE_PROFILE_HDP, "Health Device Profile (HDP) - Source");
map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SINK , "Video Distribution Profile (VDP)"); map.put(HEALTH_DEVICE_PROFILE_HDP_SOURCE, "Health Device Profile (HDP) - Sink");
map.put(VIDEO_DISTRIBUTION_PROFILE_VDP , "Health Device Profile (HDP)"); map.put(HEALTH_DEVICE_PROFILE_HDP_SINK, "GAP");
map.put(HEALTH_DEVICE_PROFILE_HDP , "Health Device Profile (HDP) - Source"); map.put(GAP, "GATT");
map.put(HEALTH_DEVICE_PROFILE_HDP_SOURCE , "Health Device Profile (HDP) - Sink"); map.put(GATT, "IMMEDIATE_ALERT");
map.put(HEALTH_DEVICE_PROFILE_HDP_SINK , "GAP"); map.put(IMMEDIATE_ALERT, "LINK_LOSS");
map.put(GAP , "GATT"); map.put(LINK_LOSS, "TX_POWER");
map.put(GATT , "IMMEDIATE_ALERT"); map.put(TX_POWER, "Health Thermometer");
map.put(IMMEDIATE_ALERT , "LINK_LOSS"); map.put(HEALTH_THERMOMETER, "Device Information");
map.put(LINK_LOSS , "TX_POWER"); map.put(DEVICE_INFORMATION, "HEART_RATE");
map.put(TX_POWER , "Health Thermometer"); map.put(HEART_RATE, "CYCLING_SC");
map.put(HEALTH_THERMOMETER , "Device Information"); map.put(CYCLING_SC, "CLIENT_CHARACTERISTIC_CONFIG");
map.put(DEVICE_INFORMATION , "HEART_RATE"); map.put(CLIENT_CHARACTERISTIC_CONFIG, "Device Name");
map.put(HEART_RATE , "CYCLING_SC"); map.put(DEVICE_NAME, "Appearance");
map.put(CYCLING_SC , "CLIENT_CHARACTERISTIC_CONFIG"); map.put(APPEARANCE, "Peripheral Privacy Flag");
map.put(CLIENT_CHARACTERISTIC_CONFIG , "Device Name"); map.put(PERIPHERAL_PRIVACY_FLAG, "Reconnection Address");
map.put(DEVICE_NAME , "Appearance"); map.put(RECONNECTION_ADDRESS, "Peripheral Preferred Connection Parameters");
map.put(APPEARANCE , "Peripheral Privacy Flag"); map.put(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS, "Service Changed");
map.put(PERIPHERAL_PRIVACY_FLAG , "Reconnection Address"); map.put(SERVICE_CHANGED, "Alert Level");
map.put(RECONNECTION_ADDRESS , "Peripheral Preferred Connection Parameters"); map.put(ALERT_LEVEL, "Tx Power Level");
map.put(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS , "Service Changed"); map.put(TX_POWER_LEVEL, "Date Time");
map.put(SERVICE_CHANGED , "Alert Level"); map.put(DATE_TIME, "Day of Week");
map.put(ALERT_LEVEL , "Tx Power Level"); map.put(DAY_OF_WEEK, "Day Date Time");
map.put(TX_POWER_LEVEL , "Date Time"); map.put(DAY_DATE_TIME, "Exact Time 256");
map.put(DATE_TIME , "Day of Week"); map.put(EXACT_TIME_256, "DST Offset");
map.put(DAY_OF_WEEK , "Day Date Time"); map.put(DST_OFFSET, "Time Zone");
map.put(DAY_DATE_TIME , "Exact Time 256"); map.put(TIME_ZONE, "Local Time Information");
map.put(EXACT_TIME_256 , "DST Offset"); map.put(LOCAL_TIME_INFORMATION, "Time with DST");
map.put(DST_OFFSET , "Time Zone"); map.put(TIME_WITH_DST, "Time Accuracy");
map.put(TIME_ZONE , "Local Time Information"); map.put(TIME_ACCURACY, "Time Source");
map.put(LOCAL_TIME_INFORMATION , "Time with DST"); map.put(TIME_SOURCE, "Reference Time Information");
map.put(TIME_WITH_DST , "Time Accuracy"); map.put(REFERENCE_TIME_INFORMATION, "Time Update Control Point");
map.put(TIME_ACCURACY , "Time Source"); map.put(TIME_UPDATE_CONTROL_POINT, "Time Update State");
map.put(TIME_SOURCE , "Reference Time Information"); map.put(TIME_UPDATE_STATE, "Temperature Measurement");
map.put(REFERENCE_TIME_INFORMATION , "Time Update Control Point"); map.put(TEMPERATURE_MEASUREMENT, "Temperature Type");
map.put(TIME_UPDATE_CONTROL_POINT , "Time Update State"); map.put(TEMPERATURE_TYPE, "Intermediate Temperature");
map.put(TIME_UPDATE_STATE , "Temperature Measurement"); map.put(INTERMEDIATE_TEMPERATURE, "Measurement Interval");
map.put(TEMPERATURE_MEASUREMENT , "Temperature Type"); map.put(MEASUREMENT_INTERVAL, "System ID");
map.put(TEMPERATURE_TYPE , "Intermediate Temperature"); map.put(SYSTEM_ID, "Model Number String");
map.put(INTERMEDIATE_TEMPERATURE , "Measurement Interval"); map.put(MODEL_NUMBER_STRING, "Serial Number String");
map.put(MEASUREMENT_INTERVAL , "System ID"); map.put(SERIAL_NUMBER_STRING, "Firmware Revision String");
map.put(SYSTEM_ID , "Model Number String"); map.put(FIRMWARE_REVISION_STRING, "Hardware Revision String");
map.put(MODEL_NUMBER_STRING , "Serial Number String"); map.put(HARDWARE_REVISION_STRING, "Software Revision String");
map.put(SERIAL_NUMBER_STRING , "Firmware Revision String"); map.put(SOFTWARE_REVISION_STRING, "Manufacturer Name String");
map.put(FIRMWARE_REVISION_STRING , "Hardware Revision String"); map.put(MANUFACTURER_NAME_STRING, "IEEE 11073-20601 Regulatory");
map.put(HARDWARE_REVISION_STRING , "Software Revision String"); map.put(IEEE_1107320601_REGULATORY, "Current Time");
map.put(SOFTWARE_REVISION_STRING , "Manufacturer Name String"); map.put(CURRENT_TIME, "Blood Pressure Measurement");
map.put(MANUFACTURER_NAME_STRING , "IEEE 11073-20601 Regulatory"); map.put(BLOOD_PRESSURE_MEASUREMENT, "Intermediate Cuff Pressure");
map.put(IEEE_1107320601_REGULATORY , "Current Time"); map.put(INTERMEDIATE_CUFF_PRESSURE, "Heart Rate Measurement");
map.put(CURRENT_TIME , "Blood Pressure Measurement"); map.put(HEART_RATE_MEASUREMENT, "Body Sensor Location");
map.put(BLOOD_PRESSURE_MEASUREMENT , "Intermediate Cuff Pressure"); map.put(BODY_SENSOR_LOCATION, "Heart Rate Control Point");
map.put(INTERMEDIATE_CUFF_PRESSURE , "Heart Rate Measurement"); map.put(HEART_RATE_CONTROL_POINT, "Alert Status");
map.put(HEART_RATE_MEASUREMENT , "Body Sensor Location"); map.put(ALERT_STATUS, "Ringer Control Point");
map.put(BODY_SENSOR_LOCATION , "Heart Rate Control Point"); map.put(RINGER_CONTROL_POINT, "Ringer Setting");
map.put(HEART_RATE_CONTROL_POINT , "Alert Status"); map.put(RINGER_SETTING, "Alert Category ID Bit Mask");
map.put(ALERT_STATUS , "Ringer Control Point"); map.put(ALERT_CATEGORY_ID_BIT_MASK, "Alert Category ID");
map.put(RINGER_CONTROL_POINT , "Ringer Setting"); map.put(ALERT_CATEGORY_ID, "Alert Notification Control Point");
map.put(RINGER_SETTING , "Alert Category ID Bit Mask"); map.put(ALERT_NOTIFICATION_CONTROL_POINT, "Unread Alert Status");
map.put(ALERT_CATEGORY_ID_BIT_MASK , "Alert Category ID"); map.put(UNREAD_ALERT_STATUS, "New Alert");
map.put(ALERT_CATEGORY_ID , "Alert Notification Control Point"); map.put(NEW_ALERT, "Supported New Alert Category");
map.put(ALERT_NOTIFICATION_CONTROL_POINT , "Unread Alert Status"); map.put(SUPPORTED_NEW_ALERT_CATEGORY, "Supported Unread Alert Category");
map.put(UNREAD_ALERT_STATUS , "New Alert"); map.put(SUPPORTED_UNREAD_ALERT_CATEGORY, "Blood Pressure Feature");
map.put(NEW_ALERT , "Supported New Alert Category"); map.put(BLOOD_PRESSURE_FEATURE, "PNPID");
map.put(SUPPORTED_NEW_ALERT_CATEGORY , "Supported Unread Alert Category"); map.put(PNPID, "SC_CONTROL_POINT");
map.put(SUPPORTED_UNREAD_ALERT_CATEGORY , "Blood Pressure Feature"); map.put(SC_CONTROL_POINT, "CSC_MEASUREMENT");
map.put(BLOOD_PRESSURE_FEATURE , "PNPID"); map.put(CSC_MEASUREMENT, "CSC_FEATURE");
map.put(PNPID , "SC_CONTROL_POINT"); map.put(CSC_FEATURE, "SENSOR_LOCATION");
map.put(SC_CONTROL_POINT , "CSC_MEASUREMENT"); map.put(SENSOR_LOCATION, "ActiveSync");
map.put(CSC_MEASUREMENT , "CSC_FEATURE"); map.put(ACTIVESYNC, "ActiveSync");
map.put(CSC_FEATURE , "SENSOR_LOCATION"); map.put(ESTIMOTE_SERVICE, "Estimote Service");
map.put(SENSOR_LOCATION , "ActiveSync"); map.put(ESTIMOTE_UUID, "Estimote UUID");
map.put(ACTIVESYNC , "ActiveSync"); map.put(ESTIMOTE_MAJOR, "Estimote Major");
map.put(ESTIMOTE_SERVICE , "Estimote Service"); map.put(ESTIMOTE_MINOR, "Estimote Minor");
map.put(ESTIMOTE_UUID , "Estimote UUID"); map.put(ESTIMOTE_BATTERY, "Estimote Battery");
map.put(ESTIMOTE_MAJOR , "Estimote Major"); map.put(ESTIMOTE_TEMPERATURE, "Estimote Temperature");
map.put(ESTIMOTE_MINOR , "Estimote Minor"); map.put(ESTIMOTE_POWER, "Estimote Power");
map.put(ESTIMOTE_BATTERY , "Estimote Battery"); map.put(ESTIMOTE_ADVERTISING_INTERVAL, "Estimote Advertising Interval");
map.put(ESTIMOTE_TEMPERATURE , "Estimote Temperature"); map.put(ESTIMOTE_VERSION_SERVICE, "Estimote Version Service");
map.put(ESTIMOTE_POWER , "Estimote Power"); map.put(ESTIMOTE_SOFTWARE_VERSION, "Estimote Software Version");
map.put(ESTIMOTE_ADVERTISING_INTERVAL , "Estimote Advertising Interval"); map.put(ESTIMOTE_HARDWARE_VERSION, "Estimote Hardware Version");
map.put(ESTIMOTE_VERSION_SERVICE , "Estimote Version Service"); map.put(ESTIMOTE_AUTHENTICATION_SERVICE, "Estimote Authentication Service");
map.put(ESTIMOTE_SOFTWARE_VERSION , "Estimote Software Version"); map.put(ESTIMOTE_ADVERTISING_SEED, "Estimote Advertising Seed");
map.put(ESTIMOTE_HARDWARE_VERSION , "Estimote Hardware Version"); map.put(ESTIMOTE_ADVERTISING_VECTOR, "Estimote Advertising Vector");
map.put(ESTIMOTE_AUTHENTICATION_SERVICE , "Estimote Authentication Service");
map.put(ESTIMOTE_ADVERTISING_SEED , "Estimote Advertising Seed");
map.put(ESTIMOTE_ADVERTISING_VECTOR , "Estimote Advertising Vector");
return map;
return map; }
}
} }
@@ -1,5 +1,8 @@
package uk.co.alt236.bluetoothlelib.util; package uk.co.alt236.bluetoothlelib.util;
import android.annotation.SuppressLint;
import android.util.SparseArray;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -8,113 +11,117 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
import android.annotation.SuppressLint;
import android.util.SparseArray;
public class AdRecordUtils { public class AdRecordUtils {
public static String getRecordDataAsString(final AdRecord nameRecord) { public static String getRecordDataAsString(final AdRecord nameRecord) {
if(nameRecord == null){return "";} if (nameRecord == null) {
return new String(nameRecord.getData()); return "";
} }
return new String(nameRecord.getData());
}
public static byte[] getServiceData(final AdRecord serviceData) { public static byte[] getServiceData(final AdRecord serviceData) {
if (serviceData == null) {return null;} if (serviceData == null) {
if (serviceData.getType() != AdRecord.TYPE_SERVICE_DATA) return null; return null;
}
if (serviceData.getType() != AdRecord.TYPE_SERVICE_DATA) return null;
final byte[] raw = serviceData.getData(); final byte[] raw = serviceData.getData();
//Chop out the uuid //Chop out the uuid
return Arrays.copyOfRange(raw, 2, raw.length); return Arrays.copyOfRange(raw, 2, raw.length);
} }
public static int getServiceDataUuid(final AdRecord serviceData) { public static int getServiceDataUuid(final AdRecord serviceData) {
if (serviceData == null) {return -1;} if (serviceData == null) {
if (serviceData.getType() != AdRecord.TYPE_SERVICE_DATA) return -1; return -1;
}
if (serviceData.getType() != AdRecord.TYPE_SERVICE_DATA) return -1;
final byte[] raw = serviceData.getData(); final byte[] raw = serviceData.getData();
//Find UUID data in byte array //Find UUID data in byte array
int uuid = (raw[1] & 0xFF) << 8; int uuid = (raw[1] & 0xFF) << 8;
uuid += (raw[0] & 0xFF); uuid += (raw[0] & 0xFF);
return uuid; return uuid;
} }
/* /*
* Read out all the AD structures from the raw scan record * Read out all the AD structures from the raw scan record
*/ */
public static List<AdRecord> parseScanRecordAsList(final byte[] scanRecord) { public static List<AdRecord> parseScanRecordAsList(final byte[] scanRecord) {
final List<AdRecord> records = new ArrayList<AdRecord>(); final List<AdRecord> records = new ArrayList<AdRecord>();
int index = 0; int index = 0;
while (index < scanRecord.length) { while (index < scanRecord.length) {
final int length = scanRecord[index++]; final int length = scanRecord[index++];
//Done once we run out of records //Done once we run out of records
if (length == 0) break; if (length == 0) break;
final int type = ByteUtils.getIntFromByte(scanRecord[index]); final int type = ByteUtils.getIntFromByte(scanRecord[index]);
//Done if our record isn't a valid type //Done if our record isn't a valid type
if (type == 0) break; if (type == 0) break;
final byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length); final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length);
records.add(new AdRecord(length, type, data)); records.add(new AdRecord(length, type, data));
//Advance //Advance
index += length; index += length;
} }
return Collections.unmodifiableList(records); return Collections.unmodifiableList(records);
} }
@SuppressLint("UseSparseArrays") @SuppressLint("UseSparseArrays")
public static Map<Integer, AdRecord> parseScanRecordAsMap(final byte[] scanRecord) { public static Map<Integer, AdRecord> parseScanRecordAsMap(final byte[] scanRecord) {
final Map<Integer, AdRecord> records = new HashMap<Integer, AdRecord>(); final Map<Integer, AdRecord> records = new HashMap<Integer, AdRecord>();
int index = 0; int index = 0;
while (index < scanRecord.length) { while (index < scanRecord.length) {
final int length = scanRecord[index++]; final int length = scanRecord[index++];
//Done once we run out of records //Done once we run out of records
if (length == 0) break; if (length == 0) break;
final int type = ByteUtils.getIntFromByte(scanRecord[index]); final int type = ByteUtils.getIntFromByte(scanRecord[index]);
//Done if our record isn't a valid type //Done if our record isn't a valid type
if (type == 0) break; if (type == 0) break;
final byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length); final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length);
records.put(type, new AdRecord(length, type, data)); records.put(type, new AdRecord(length, type, data));
//Advance //Advance
index += length; index += length;
} }
return Collections.unmodifiableMap(records); return Collections.unmodifiableMap(records);
} }
public static SparseArray<AdRecord> parseScanRecordAsSparseArray(final byte[] scanRecord) { public static SparseArray<AdRecord> parseScanRecordAsSparseArray(final byte[] scanRecord) {
final SparseArray<AdRecord> records = new SparseArray<AdRecord>(); final SparseArray<AdRecord> records = new SparseArray<AdRecord>();
int index = 0; int index = 0;
while (index < scanRecord.length) { while (index < scanRecord.length) {
final int length = scanRecord[index++]; final int length = scanRecord[index++];
//Done once we run out of records //Done once we run out of records
if (length == 0) break; if (length == 0) break;
final int type = ByteUtils.getIntFromByte(scanRecord[index]); final int type = ByteUtils.getIntFromByte(scanRecord[index]);
//Done if our record isn't a valid type //Done if our record isn't a valid type
if (type == 0) break; if (type == 0) break;
final byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length); final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length);
records.put(type, new AdRecord(length, type, data)); records.put(type, new AdRecord(length, type, data));
//Advance //Advance
index += length; index += length;
} }
return records; return records;
} }
} }
@@ -4,121 +4,124 @@ import java.nio.ByteBuffer;
public class ByteUtils { public class ByteUtils {
/** The Constant HEXES. */ /**
private static final String HEXES = "0123456789ABCDEF"; * The Constant HEXES.
*/
private static final String HEXES = "0123456789ABCDEF";
/** /**
* Gets a pretty representation of a Byte Array as a HEX String. * Gets a pretty representation of a Byte Array as a HEX String.
* * <p/>
* Sample output: [01, 30, FF, AA] * Sample output: [01, 30, FF, AA]
* *
* @param array the array * @param array the array
* @return the string * @return the string
*/ */
public static String byteArrayToHexString(final byte[] array){ public static String byteArrayToHexString(final byte[] array) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
boolean firstEntry = true; boolean firstEntry = true;
sb.append('['); sb.append('[');
for ( final byte b : array ) { for (final byte b : array) {
if(!firstEntry){ if (!firstEntry) {
sb.append(", "); sb.append(", ");
} }
sb.append(HEXES.charAt((b & 0xF0) >> 4)); sb.append(HEXES.charAt((b & 0xF0) >> 4));
sb.append(HEXES.charAt((b & 0x0F))); sb.append(HEXES.charAt((b & 0x0F)));
firstEntry = false; firstEntry = false;
} }
sb.append(']'); sb.append(']');
return sb.toString(); return sb.toString();
} }
/** /**
* Checks to see if a byte arry starts with another byte array. * Checks to see if a byte arry starts with another byte array.
* *
* @param array the array * @param array the array
* @param prefix the prefix * @param prefix the prefix
* @return true, if successful * @return true, if successful
*/ */
public static boolean doesArrayBeginWith(final byte[] array, final byte[] prefix){ public static boolean doesArrayBeginWith(final byte[] array, final byte[] prefix) {
if(array.length < prefix.length){return false;} if (array.length < prefix.length) {
return false;
}
for(int i = 0; i < prefix.length; i++){ for (int i = 0; i < prefix.length; i++) {
if(array[i] != prefix[i]){ if (array[i] != prefix[i]) {
return false; return false;
} }
} }
return true; return true;
} }
/** /**
* Converts a byte array with a length of 2 into an int * Converts a byte array with a length of 2 into an int
* *
* @param input the input * @param input the input
* @return the int from the array * @return the int from the array
*/ */
public static int getIntFrom2ByteArray(final byte[] input){ public static int getIntFrom2ByteArray(final byte[] input) {
final byte[] result = new byte[4]; final byte[] result = new byte[4];
result[0] = 0; result[0] = 0;
result[1] = 0; result[1] = 0;
result[2] = input[0]; result[2] = input[0];
result[3] = input[1]; result[3] = input[1];
return ByteUtils.getIntFromByteArray(result); return ByteUtils.getIntFromByteArray(result);
} }
/** /**
* Converts a byte to an int, preserving the sign. * Converts a byte to an int, preserving the sign.
* * <p/>
* For example, FF will be converted to 255 and not -1. * For example, FF will be converted to 255 and not -1.
* *
* @param bite the bite * @param bite the bite
* @return the int from byte * @return the int from byte
*/ */
public static int getIntFromByte(final byte bite){ public static int getIntFromByte(final byte bite) {
return Integer.valueOf(bite & 0xFF); return Integer.valueOf(bite & 0xFF);
} }
/** /**
* Converts a byte array to an int. * Converts a byte array to an int.
* *
* @param bytes the bytes * @param bytes the bytes
* @return the int from byte array * @return the int from byte array
*/ */
public static int getIntFromByteArray(final byte[] bytes) { public static int getIntFromByteArray(final byte[] bytes) {
return ByteBuffer.wrap(bytes).getInt(); return ByteBuffer.wrap(bytes).getInt();
} }
/** /**
* Converts a byte array to a long. * Converts a byte array to a long.
* *
* @param bytes the bytes * @param bytes the bytes
* @return the long from byte array * @return the long from byte array
*/ */
public static long getLongFromByteArray(final byte[] bytes) { public static long getLongFromByteArray(final byte[] bytes) {
return ByteBuffer.wrap(bytes).getLong(); return ByteBuffer.wrap(bytes).getLong();
} }
/** /**
* Inverts an array * Inverts an array
* *
* @param array the array * @param array the array
* @return the byte[] * @return the byte[]
*/ */
public static byte[] invertArray(final byte[] array){ public static byte[] invertArray(final byte[] array) {
final int size = array.length; final int size = array.length;
byte temp; byte temp;
for (int i = 0; i < size/2; i++) for (int i = 0; i < size / 2; i++) {
{ temp = array[i];
temp = array[i]; array[i] = array[size - 1 - i];
array[i] = array[size-1 - i]; array[size - 1 - i] = temp;
array[size-1 - i] = temp; }
}
return array; return array;
} }
} }
@@ -0,0 +1,8 @@
package uk.co.alt236.bluetoothlelib.util;
public enum IBeaconDistanceDescriptor {
IMMEDIATE,
NEAR,
FAR,
UNKNOWN,
}
@@ -4,86 +4,83 @@ import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
public class IBeaconUtils { public class IBeaconUtils {
private static final double DISTANCE_THRESHOLD_WTF = 0.0; private static final double DISTANCE_THRESHOLD_WTF = 0.0;
private static final double DISTANCE_THRESHOLD_IMMEDIATE = 0.5; private static final double DISTANCE_THRESHOLD_IMMEDIATE = 0.5;
private static final double DISTANCE_THRESHOLD_NEAR = 3.0; private static final double DISTANCE_THRESHOLD_NEAR = 3.0;
private static final byte[] MANUFACTURER_DATA_IBEACON_PREFIX = new byte[]{0x4C, 0x00, 0x02, 0x15}; private static final byte[] MANUFACTURER_DATA_IBEACON_PREFIX = new byte[]{0x4C, 0x00, 0x02, 0x15};
public static IBeaconDistanceDescriptor getDistanceDescriptor(final double accuracy){ /**
if(accuracy < DISTANCE_THRESHOLD_WTF){ * Calculates the accuracy of an RSSI reading.
return IBeaconDistanceDescriptor.UNKNOWN; * <p/>
} * The code was taken from {@linktourl http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing}
*
* @param txPower the calibrated TX power of an iBeacon
* @param rssi the RSSI value of the iBeacon
* @return the calculated Accuracy
*/
public static double calculateAccuracy(final int txPower, final double rssi) {
if (rssi == 0) {
return -1.0; // if we cannot determine accuracy, return -1.
}
if(accuracy < DISTANCE_THRESHOLD_IMMEDIATE){ final double ratio = rssi * 1.0 / txPower;
return IBeaconDistanceDescriptor.IMMEDIATE; if (ratio < 1.0) {
} return Math.pow(ratio, 10);
} else {
final double accuracy = (0.89976) * Math.pow(ratio, 7.7095) + 0.111;
return accuracy;
}
}
if(accuracy < DISTANCE_THRESHOLD_NEAR){ public static IBeaconDistanceDescriptor getDistanceDescriptor(final double accuracy) {
return IBeaconDistanceDescriptor.NEAR; if (accuracy < DISTANCE_THRESHOLD_WTF) {
} return IBeaconDistanceDescriptor.UNKNOWN;
}
return IBeaconDistanceDescriptor.FAR; if (accuracy < DISTANCE_THRESHOLD_IMMEDIATE) {
} return IBeaconDistanceDescriptor.IMMEDIATE;
}
/** if (accuracy < DISTANCE_THRESHOLD_NEAR) {
* Calculates the accuracy of an RSSI reading. return IBeaconDistanceDescriptor.NEAR;
* }
* The code was taken from {@linktourl http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing}
*
* @param txPower the calibrated TX power of an iBeacon
* @param rssi the RSSI value of the iBeacon
* @return the calculated Accuracy
*/
public static double calculateAccuracy(final int txPower, final double rssi) {
if (rssi == 0) {
return -1.0; // if we cannot determine accuracy, return -1.
}
final double ratio = rssi*1.0/txPower; return IBeaconDistanceDescriptor.FAR;
if (ratio < 1.0) { }
return Math.pow(ratio,10);
}
else {
final double accuracy = (0.89976)*Math.pow(ratio,7.7095) + 0.111;
return accuracy;
}
}
/** /**
* Ascertains whether a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} is an iBeacon; * Ascertains whether a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} is an iBeacon;
* *
* @param device a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} device. * @param device a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} device.
* @return true if the device is an iBeacon, false otherwise * @return true if the device is an iBeacon, false otherwise
*/ */
public static boolean isThisAnIBeacon(final BluetoothLeDevice device){ public static boolean isThisAnIBeacon(final BluetoothLeDevice device) {
return isThisAnIBeacon( return isThisAnIBeacon(
device.getAdRecordStore().getRecordDataAsString(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getBytes()); device.getAdRecordStore().getRecordDataAsString(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getBytes());
} }
/** /**
* Ascertains whether a Manufacturer Data byte array belongs to an iBeacon; * Ascertains whether a Manufacturer Data byte array belongs to an iBeacon;
* *
* @param manufacturerData a Bluetooth LE device's raw manufacturerData. * @param manufacturerData a Bluetooth LE device's raw manufacturerData.
* @return true if the manufacturer data belong to an iBeacon * @return true if the manufacturer data belong to an iBeacon
*/ */
public static boolean isThisAnIBeacon(final byte[] manufacturerData){ public static boolean isThisAnIBeacon(final byte[] manufacturerData) {
if(manufacturerData == null){return false;} if (manufacturerData == null) {
return false;
}
// An iBeacon record must be at least 25 chars long // An iBeacon record must be at least 25 chars long
if(!(manufacturerData.length >= 25)){return false;} if (!(manufacturerData.length >= 25)) {
return false;
}
if(ByteUtils.doesArrayBeginWith(manufacturerData, MANUFACTURER_DATA_IBEACON_PREFIX)){ if (ByteUtils.doesArrayBeginWith(manufacturerData, MANUFACTURER_DATA_IBEACON_PREFIX)) {
return true; return true;
} }
return false; return false;
} }
public enum IBeaconDistanceDescriptor{
IMMEDIATE,
NEAR,
FAR,
UNKNOWN,
}
} }
@@ -3,18 +3,18 @@ package uk.co.alt236.bluetoothlelib.util;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
public class LimitedLinkHashMap<K, V> extends LinkedHashMap<K, V>{ public class LimitedLinkHashMap<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = -5375660288461724925L; private static final long serialVersionUID = -5375660288461724925L;
private final int mMaxSize; private final int mMaxSize;
public LimitedLinkHashMap(final int maxSize){ public LimitedLinkHashMap(final int maxSize) {
super(maxSize + 1, 1, false); super(maxSize + 1, 1, false);
mMaxSize = maxSize; mMaxSize = maxSize;
} }
@Override @Override
protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) { protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
return this.size() > mMaxSize; return this.size() > mMaxSize;
} }
} }
+14 -13
View File
@@ -1,43 +1,44 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
package="uk.co.alt236.btlescan" package="uk.co.alt236.btlescan"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="3" android:versionCode="3"
android:versionName="0.0.3" > android:versionName="0.0.3">
<uses-sdk <uses-sdk
android:minSdkVersion="18" android:minSdkVersion="18"
android:targetSdkVersion="18" /> android:targetSdkVersion="18"/>
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature <uses-feature
android:name="android.hardware.bluetooth_le" android:name="android.hardware.bluetooth_le"
android:required="false" /> android:required="false"/>
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" > android:theme="@style/AppTheme">
<activity <activity
android:name="uk.co.alt236.btlescan.activities.MainActivity" android:name="uk.co.alt236.btlescan.activities.MainActivity"
android:label="@string/app_name" > android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name="uk.co.alt236.btlescan.activities.DeviceDetailsActivity" android:name="uk.co.alt236.btlescan.activities.DeviceDetailsActivity"
android:label="@string/app_name" > android:label="@string/app_name">
</activity> </activity>
<activity android:name="uk.co.alt236.btlescan.activities.DeviceControlActivity" /> <activity android:name="uk.co.alt236.btlescan.activities.DeviceControlActivity"/>
<service <service
android:name="uk.co.alt236.btlescan.services.BluetoothLeService" android:name="uk.co.alt236.btlescan.services.BluetoothLeService"
android:enabled="true" /> android:enabled="true"/>
</application> </application>
</manifest> </manifest>
@@ -16,16 +16,6 @@
package uk.co.alt236.btlescan.activities; package uk.co.alt236.btlescan.activities;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.bluetoothlelib.resolvers.GattAttributeResolver;
import uk.co.alt236.bluetoothlelib.util.ByteUtils;
import uk.co.alt236.btlescan.R;
import uk.co.alt236.btlescan.services.BluetoothLeService;
import android.app.Activity; import android.app.Activity;
import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothGattService;
@@ -44,8 +34,19 @@ import android.view.View;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.SimpleExpandableListAdapter; import android.widget.SimpleExpandableListAdapter;
import android.widget.TextView; import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.InjectView; import butterknife.InjectView;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.bluetoothlelib.resolvers.GattAttributeResolver;
import uk.co.alt236.bluetoothlelib.util.ByteUtils;
import uk.co.alt236.btlescan.R;
import uk.co.alt236.btlescan.services.BluetoothLeService;
/** /**
* For a given BLE device, this Activity provides the user interface to connect, display data, * For a given BLE device, this Activity provides the user interface to connect, display data,
@@ -54,358 +55,356 @@ import butterknife.InjectView;
* Bluetooth LE API. * Bluetooth LE API.
*/ */
public class DeviceControlActivity extends Activity { public class DeviceControlActivity extends Activity {
private final static String TAG = DeviceControlActivity.class.getSimpleName(); public static final String EXTRA_DEVICE = "extra_device";
public static final String EXTRA_DEVICE = "extra_device"; private final static String TAG = DeviceControlActivity.class.getSimpleName();
private static final String LIST_NAME = "NAME";
private static final String LIST_UUID = "UUID";
@InjectView(R.id.gatt_services_list)
ExpandableListView mGattServicesList;
@InjectView(R.id.connection_state)
TextView mConnectionState;
@InjectView(R.id.uuid)
TextView mGattUUID;
@InjectView(R.id.description)
TextView mGattUUIDDesc;
@InjectView(R.id.data_as_string)
TextView mDataAsString;
@InjectView(R.id.data_as_array)
TextView mDataAsArray;
private BluetoothGattCharacteristic mNotifyCharacteristic;
private BluetoothLeService mBluetoothLeService;
private List<List<BluetoothGattCharacteristic>> mGattCharacteristics = new ArrayList<List<BluetoothGattCharacteristic>>();
// If a given GATT characteristic is selected, check for supported features. This sample
// demonstrates 'Read' and 'Notify' features. See
// http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete
// list of supported characteristic features.
private final ExpandableListView.OnChildClickListener servicesListClickListner = new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(final ExpandableListView parent, final View v, final int groupPosition, final int childPosition, final long id) {
if (mGattCharacteristics != null) {
final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(groupPosition).get(childPosition);
final int charaProp = characteristic.getProperties();
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
// If there is an active notification on a characteristic, clear
// it first so it doesn't update the data field on the user interface.
if (mNotifyCharacteristic != null) {
mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, false);
mNotifyCharacteristic = null;
}
mBluetoothLeService.readCharacteristic(characteristic);
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
mNotifyCharacteristic = characteristic;
mBluetoothLeService.setCharacteristicNotification(characteristic, true);
}
return true;
}
return false;
}
};
private String mDeviceAddress;
// Code to manage Service lifecycle.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(final ComponentName componentName, final IBinder service) {
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (!mBluetoothLeService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// Automatically connects to the device upon successful start-up initialization.
mBluetoothLeService.connect(mDeviceAddress);
}
private static final String LIST_NAME = "NAME"; @Override
private static final String LIST_UUID = "UUID"; public void onServiceDisconnected(final ComponentName componentName) {
mBluetoothLeService = null;
}
};
private String mDeviceName;
private boolean mConnected = false;
private String mExportString;
// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device.
// this can be a result of read or notification operations.
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
mConnected = true;
updateConnectionState(R.string.connected);
invalidateOptionsMenu();
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false;
updateConnectionState(R.string.disconnected);
invalidateOptionsMenu();
clearUI();
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
// Show all the supported services and characteristics on the user interface.
displayGattServices(mBluetoothLeService.getSupportedGattServices());
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
final String noData = getString(R.string.no_data);
final String uuid = intent.getStringExtra(BluetoothLeService.EXTRA_UUID_CHAR);
final byte[] dataArr = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA_RAW);
private BluetoothGattCharacteristic mNotifyCharacteristic; mGattUUID.setText(tryString(uuid, noData));
private BluetoothLeService mBluetoothLeService; mGattUUIDDesc.setText(GattAttributeResolver.getAttributeName(uuid, getString(R.string.unknown)));
private List<List<BluetoothGattCharacteristic>> mGattCharacteristics = new ArrayList<List<BluetoothGattCharacteristic>>(); mDataAsArray.setText(ByteUtils.byteArrayToHexString(dataArr));
private String mDeviceAddress; mDataAsString.setText(new String(dataArr));
private String mDeviceName; }
}
};
@InjectView(R.id.gatt_services_list) ExpandableListView mGattServicesList; private void clearUI() {
@InjectView(R.id.connection_state) TextView mConnectionState; mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
mGattUUID.setText(R.string.no_data);
mGattUUIDDesc.setText(R.string.no_data);
mDataAsArray.setText(R.string.no_data);
mDataAsString.setText(R.string.no_data);
}
@InjectView(R.id.uuid) TextView mGattUUID; // Demonstrates how to iterate through the supported GATT Services/Characteristics.
@InjectView(R.id.description) TextView mGattUUIDDesc; // In this sample, we populate the data structure that is bound to the ExpandableListView
@InjectView(R.id.data_as_string) TextView mDataAsString; // on the UI.
@InjectView(R.id.data_as_array) TextView mDataAsArray; private void displayGattServices(final List<BluetoothGattService> gattServices) {
if (gattServices == null) return;
generateExportString(gattServices);
private boolean mConnected = false; String uuid = null;
private String mExportString; final String unknownServiceString = getResources().getString(R.string.unknown_service);
final String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
final List<Map<String, String>> gattServiceData = new ArrayList<Map<String, String>>();
final List<List<Map<String, String>>> gattCharacteristicData = new ArrayList<List<Map<String, String>>>();
mGattCharacteristics = new ArrayList<List<BluetoothGattCharacteristic>>();
// Code to manage Service lifecycle. // Loops through available GATT Services.
private final ServiceConnection mServiceConnection = new ServiceConnection() { for (final BluetoothGattService gattService : gattServices) {
@Override final Map<String, String> currentServiceData = new HashMap<String, String>();
public void onServiceConnected(final ComponentName componentName, final IBinder service) { uuid = gattService.getUuid().toString();
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService(); currentServiceData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownServiceString));
if (!mBluetoothLeService.initialize()) { currentServiceData.put(LIST_UUID, uuid);
Log.e(TAG, "Unable to initialize Bluetooth"); gattServiceData.add(currentServiceData);
finish();
}
// Automatically connects to the device upon successful start-up initialization.
mBluetoothLeService.connect(mDeviceAddress);
}
@Override final List<Map<String, String>> gattCharacteristicGroupData = new ArrayList<Map<String, String>>();
public void onServiceDisconnected(final ComponentName componentName) { final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
mBluetoothLeService = null; final List<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>();
}
};
// Handles various events fired by the Service. // Loops through available Characteristics.
// ACTION_GATT_CONNECTED: connected to a GATT server. for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server. charas.add(gattCharacteristic);
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services. final Map<String, String> currentCharaData = new HashMap<String, String>();
// ACTION_DATA_AVAILABLE: received data from the device. uuid = gattCharacteristic.getUuid().toString();
// this can be a result of read or notification operations. currentCharaData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownCharaString));
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { currentCharaData.put(LIST_UUID, uuid);
@Override gattCharacteristicGroupData.add(currentCharaData);
public void onReceive(final Context context, final Intent intent) { }
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
mConnected = true;
updateConnectionState(R.string.connected);
invalidateOptionsMenu();
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false;
updateConnectionState(R.string.disconnected);
invalidateOptionsMenu();
clearUI();
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
// Show all the supported services and characteristics on the user interface.
displayGattServices(mBluetoothLeService.getSupportedGattServices());
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
final String noData = getString(R.string.no_data);
final String uuid = intent.getStringExtra(BluetoothLeService.EXTRA_UUID_CHAR);
final byte[] dataArr = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA_RAW);
mGattUUID.setText(tryString(uuid, noData)); mGattCharacteristics.add(charas);
mGattUUIDDesc.setText(GattAttributeResolver.getAttributeName(uuid, getString(R.string.unknown))); gattCharacteristicData.add(gattCharacteristicGroupData);
mDataAsArray.setText(ByteUtils.byteArrayToHexString(dataArr)); }
mDataAsString.setText(new String(dataArr));
}
}
};
// If a given GATT characteristic is selected, check for supported features. This sample final SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
// demonstrates 'Read' and 'Notify' features. See this,
// http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete gattServiceData,
// list of supported characteristic features. android.R.layout.simple_expandable_list_item_2,
private final ExpandableListView.OnChildClickListener servicesListClickListner = new ExpandableListView.OnChildClickListener() { new String[]{LIST_NAME, LIST_UUID},
@Override new int[]{android.R.id.text1, android.R.id.text2},
public boolean onChildClick(final ExpandableListView parent, final View v, final int groupPosition, final int childPosition, final long id) { gattCharacteristicData,
if (mGattCharacteristics != null) { android.R.layout.simple_expandable_list_item_2,
final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(groupPosition).get(childPosition); new String[]{LIST_NAME, LIST_UUID},
final int charaProp = characteristic.getProperties(); new int[]{android.R.id.text1, android.R.id.text2}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) { );
// If there is an active notification on a characteristic, clear
// it first so it doesn't update the data field on the user interface.
if (mNotifyCharacteristic != null) {
mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, false);
mNotifyCharacteristic = null;
}
mBluetoothLeService.readCharacteristic(characteristic);
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
mNotifyCharacteristic = characteristic;
mBluetoothLeService.setCharacteristicNotification(characteristic, true);
}
return true;
}
return false;
}
};
private void clearUI() { mGattServicesList.setAdapter(gattServiceAdapter);
mGattServicesList.setAdapter((SimpleExpandableListAdapter) null); invalidateOptionsMenu();
mGattUUID.setText(R.string.no_data); }
mGattUUIDDesc.setText(R.string.no_data);
mDataAsArray.setText(R.string.no_data);
mDataAsString.setText(R.string.no_data);
}
private void generateExportString(final List<BluetoothGattService> gattServices){ private void generateExportString(final List<BluetoothGattService> gattServices) {
final String unknownServiceString = getResources().getString(R.string.unknown_service); final String unknownServiceString = getResources().getString(R.string.unknown_service);
final String unknownCharaString = getResources().getString(R.string.unknown_characteristic); final String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
final StringBuilder exportBuilder = new StringBuilder(); final StringBuilder exportBuilder = new StringBuilder();
exportBuilder.append("Device Name: "); exportBuilder.append("Device Name: ");
exportBuilder.append(mDeviceName); exportBuilder.append(mDeviceName);
exportBuilder.append('\n'); exportBuilder.append('\n');
exportBuilder.append("Device Address: "); exportBuilder.append("Device Address: ");
exportBuilder.append(mDeviceAddress); exportBuilder.append(mDeviceAddress);
exportBuilder.append('\n'); exportBuilder.append('\n');
exportBuilder.append('\n'); exportBuilder.append('\n');
exportBuilder.append("Services:"); exportBuilder.append("Services:");
exportBuilder.append("--------------------------"); exportBuilder.append("--------------------------");
exportBuilder.append('\n'); exportBuilder.append('\n');
String uuid = null; String uuid = null;
for (final BluetoothGattService gattService : gattServices) { for (final BluetoothGattService gattService : gattServices) {
uuid = gattService.getUuid().toString(); uuid = gattService.getUuid().toString();
exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownServiceString)); exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownServiceString));
exportBuilder.append(" ("); exportBuilder.append(" (");
exportBuilder.append(uuid); exportBuilder.append(uuid);
exportBuilder.append(')'); exportBuilder.append(')');
exportBuilder.append('\n'); exportBuilder.append('\n');
final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
uuid = gattCharacteristic.getUuid().toString(); uuid = gattCharacteristic.getUuid().toString();
exportBuilder.append('\t'); exportBuilder.append('\t');
exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownCharaString)); exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownCharaString));
exportBuilder.append(" ("); exportBuilder.append(" (");
exportBuilder.append(uuid); exportBuilder.append(uuid);
exportBuilder.append(')'); exportBuilder.append(')');
exportBuilder.append('\n'); exportBuilder.append('\n');
} }
exportBuilder.append('\n'); exportBuilder.append('\n');
exportBuilder.append('\n'); exportBuilder.append('\n');
} }
exportBuilder.append("--------------------------"); exportBuilder.append("--------------------------");
exportBuilder.append('\n'); exportBuilder.append('\n');
mExportString = exportBuilder.toString(); mExportString = exportBuilder.toString();
} }
// Demonstrates how to iterate through the supported GATT Services/Characteristics. @Override
// In this sample, we populate the data structure that is bound to the ExpandableListView public void onCreate(final Bundle savedInstanceState) {
// on the UI. super.onCreate(savedInstanceState);
private void displayGattServices(final List<BluetoothGattService> gattServices) { setContentView(R.layout.activity_gatt_services);
if (gattServices == null) return;
generateExportString(gattServices);
String uuid = null; final Intent intent = getIntent();
final String unknownServiceString = getResources().getString(R.string.unknown_service); final BluetoothLeDevice device = intent.getParcelableExtra(EXTRA_DEVICE);
final String unknownCharaString = getResources().getString(R.string.unknown_characteristic); mDeviceName = device.getName();
final List<Map<String, String>> gattServiceData = new ArrayList<Map<String, String>>(); mDeviceAddress = device.getAddress();
final List<List<Map<String, String>>> gattCharacteristicData = new ArrayList<List<Map<String, String>>>();
mGattCharacteristics = new ArrayList<List<BluetoothGattCharacteristic>>();
// Loops through available GATT Services. ButterKnife.inject(this);
for (final BluetoothGattService gattService : gattServices) {
final Map<String, String> currentServiceData = new HashMap<String, String>();
uuid = gattService.getUuid().toString();
currentServiceData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownServiceString));
currentServiceData.put(LIST_UUID, uuid);
gattServiceData.add(currentServiceData);
final List<Map<String, String>> gattCharacteristicGroupData = new ArrayList<Map<String, String>>(); // Sets up UI references.
final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
final List<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>(); mGattServicesList.setOnChildClickListener(servicesListClickListner);
// Loops through available Characteristics. getActionBar().setTitle(mDeviceName);
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { getActionBar().setDisplayHomeAsUpEnabled(true);
charas.add(gattCharacteristic);
final Map<String, String> currentCharaData = new HashMap<String, String>();
uuid = gattCharacteristic.getUuid().toString();
currentCharaData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownCharaString));
currentCharaData.put(LIST_UUID, uuid);
gattCharacteristicGroupData.add(currentCharaData);
}
mGattCharacteristics.add(charas); final Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
gattCharacteristicData.add(gattCharacteristicGroupData); bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
} }
final SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter( @Override
this, public boolean onCreateOptionsMenu(final Menu menu) {
gattServiceData, getMenuInflater().inflate(R.menu.gatt_services, menu);
android.R.layout.simple_expandable_list_item_2, if (mConnected) {
new String[] {LIST_NAME, LIST_UUID}, menu.findItem(R.id.menu_connect).setVisible(false);
new int[] { android.R.id.text1, android.R.id.text2 }, menu.findItem(R.id.menu_disconnect).setVisible(true);
gattCharacteristicData, } else {
android.R.layout.simple_expandable_list_item_2, menu.findItem(R.id.menu_connect).setVisible(true);
new String[] {LIST_NAME, LIST_UUID}, menu.findItem(R.id.menu_disconnect).setVisible(false);
new int[] { android.R.id.text1, android.R.id.text2 } }
);
mGattServicesList.setAdapter(gattServiceAdapter); if (mExportString == null) {
invalidateOptionsMenu(); menu.findItem(R.id.menu_share).setVisible(false);
} } else {
menu.findItem(R.id.menu_share).setVisible(true);
}
@Override return true;
public void onCreate(final Bundle savedInstanceState) { }
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gatt_services);
final Intent intent = getIntent(); @Override
final BluetoothLeDevice device = intent.getParcelableExtra(EXTRA_DEVICE); protected void onDestroy() {
mDeviceName = device.getName(); super.onDestroy();
mDeviceAddress = device.getAddress(); unbindService(mServiceConnection);
mBluetoothLeService = null;
}
ButterKnife.inject(this); @Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_connect:
mBluetoothLeService.connect(mDeviceAddress);
return true;
case R.id.menu_disconnect:
mBluetoothLeService.disconnect();
return true;
case android.R.id.home:
onBackPressed();
return true;
case R.id.menu_share:
final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
final String subject = getString(R.string.exporter_email_device_services_subject, mDeviceName, mDeviceAddress);
// Sets up UI references. intent.setType("text/plain");
((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress); intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
mGattServicesList.setOnChildClickListener(servicesListClickListner); intent.putExtra(android.content.Intent.EXTRA_TEXT, mExportString);
getActionBar().setTitle(mDeviceName); startActivity(Intent.createChooser(
getActionBar().setDisplayHomeAsUpEnabled(true); intent,
getString(R.string.exporter_email_device_list_picker_text)));
final Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); return true;
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE); }
} return super.onOptionsItemSelected(item);
}
@Override @Override
public boolean onCreateOptionsMenu(final Menu menu) { protected void onPause() {
getMenuInflater().inflate(R.menu.gatt_services, menu); super.onPause();
if (mConnected) { unregisterReceiver(mGattUpdateReceiver);
menu.findItem(R.id.menu_connect).setVisible(false); }
menu.findItem(R.id.menu_disconnect).setVisible(true);
} else {
menu.findItem(R.id.menu_connect).setVisible(true);
menu.findItem(R.id.menu_disconnect).setVisible(false);
}
if(mExportString == null){ @Override
menu.findItem(R.id.menu_share).setVisible(false); protected void onResume() {
} else { super.onResume();
menu.findItem(R.id.menu_share).setVisible(true); registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
} if (mBluetoothLeService != null) {
final boolean result = mBluetoothLeService.connect(mDeviceAddress);
Log.d(TAG, "Connect request result=" + result);
}
}
return true; private void updateConnectionState(final int resourceId) {
} runOnUiThread(new Runnable() {
@Override
public void run() {
final int colourId;
@Override switch (resourceId) {
protected void onDestroy() { case R.string.connected:
super.onDestroy(); colourId = android.R.color.holo_green_dark;
unbindService(mServiceConnection); break;
mBluetoothLeService = null; case R.string.disconnected:
} colourId = android.R.color.holo_red_dark;
break;
default:
colourId = android.R.color.black;
break;
}
@Override mConnectionState.setText(resourceId);
public boolean onOptionsItemSelected(final MenuItem item) { mConnectionState.setTextColor(getResources().getColor(colourId));
switch(item.getItemId()) { }
case R.id.menu_connect: });
mBluetoothLeService.connect(mDeviceAddress); }
return true;
case R.id.menu_disconnect:
mBluetoothLeService.disconnect();
return true;
case android.R.id.home:
onBackPressed();
return true;
case R.id.menu_share:
final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
final String subject = getString(R.string.exporter_email_device_services_subject, mDeviceName, mDeviceAddress);
intent.setType("text/plain"); private static IntentFilter makeGattUpdateIntentFilter() {
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); final IntentFilter intentFilter = new IntentFilter();
intent.putExtra(android.content.Intent.EXTRA_TEXT, mExportString); intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
return intentFilter;
}
startActivity(Intent.createChooser( private static String tryString(final String string, final String fallback) {
intent, if (string == null) {
getString(R.string.exporter_email_device_list_picker_text))); return fallback;
} else {
return true; return string;
} }
return super.onOptionsItemSelected(item); }
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
if (mBluetoothLeService != null) {
final boolean result = mBluetoothLeService.connect(mDeviceAddress);
Log.d(TAG, "Connect request result=" + result);
}
}
private void updateConnectionState(final int resourceId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
final int colourId;
switch(resourceId){
case R.string.connected:
colourId = android.R.color.holo_green_dark;
break;
case R.string.disconnected:
colourId = android.R.color.holo_red_dark;
break;
default:
colourId = android.R.color.black;
break;
}
mConnectionState.setText(resourceId);
mConnectionState.setTextColor(getResources().getColor(colourId));
}
});
}
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_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
return intentFilter;
}
private static String tryString(final String string, final String fallback){
if(string == null){
return fallback;
} else{
return string;
}
}
} }
@@ -1,8 +1,19 @@
package uk.co.alt236.btlescan.activities; package uk.co.alt236.btlescan.activities;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.commonsware.cwac.merge.MergeAdapter;
import java.util.Collection; import java.util.Collection;
import java.util.Locale; import java.util.Locale;
import butterknife.ButterKnife;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
import uk.co.alt236.bluetoothlelib.device.mfdata.IBeaconManufacturerData; import uk.co.alt236.bluetoothlelib.device.mfdata.IBeaconManufacturerData;
@@ -12,195 +23,185 @@ import uk.co.alt236.bluetoothlelib.util.ByteUtils;
import uk.co.alt236.bluetoothlelib.util.IBeaconUtils; import uk.co.alt236.bluetoothlelib.util.IBeaconUtils;
import uk.co.alt236.btlescan.R; import uk.co.alt236.btlescan.R;
import uk.co.alt236.btlescan.util.TimeFormatter; import uk.co.alt236.btlescan.util.TimeFormatter;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.TextView;
import butterknife.ButterKnife;
import com.commonsware.cwac.merge.MergeAdapter; public class DeviceDetailsActivity extends ListActivity {
public static final String EXTRA_DEVICE = "extra_device";
public class DeviceDetailsActivity extends ListActivity{ private BluetoothLeDevice mDevice;
public static final String EXTRA_DEVICE = "extra_device";
private BluetoothLeDevice mDevice; private void appendAdRecordView(final MergeAdapter adapter, final String title, final AdRecord record) {
final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_adrecord, null);
final TextView tvString = (TextView) lt.findViewById(R.id.data_as_string);
final TextView tvArray = (TextView) lt.findViewById(R.id.data_as_array);
final TextView tvTitle = (TextView) lt.findViewById(R.id.title);
private void appendAdRecordView(final MergeAdapter adapter, final String title, final AdRecord record){ tvTitle.setText(title);
final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_adrecord, null); tvString.setText("'" + AdRecordUtils.getRecordDataAsString(record) + "'");
final TextView tvString = (TextView) lt.findViewById(R.id.data_as_string); tvArray.setText("'" + ByteUtils.byteArrayToHexString(record.getData()) + "'");
final TextView tvArray = (TextView) lt.findViewById(R.id.data_as_array);
final TextView tvTitle = (TextView) lt.findViewById(R.id.title);
tvTitle.setText(title ); adapter.addView(lt);
tvString.setText("'" + AdRecordUtils.getRecordDataAsString(record) + "'"); }
tvArray.setText("'" + ByteUtils.byteArrayToHexString(record.getData()) + "'");
adapter.addView(lt); private void appendDeviceInfo(final MergeAdapter adapter, final BluetoothLeDevice device) {
} final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_device_info, null);
final TextView tvName = (TextView) lt.findViewById(R.id.deviceName);
final TextView tvAddress = (TextView) lt.findViewById(R.id.deviceAddress);
final TextView tvClass = (TextView) lt.findViewById(R.id.deviceClass);
final TextView tvBondingState = (TextView) lt.findViewById(R.id.deviceBondingState);
private void appendDeviceInfo(final MergeAdapter adapter, final BluetoothLeDevice device){ tvName.setText(device.getName());
final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_device_info, null); tvAddress.setText(device.getAddress());
final TextView tvName = (TextView) lt.findViewById(R.id.deviceName); tvClass.setText(device.getBluetoothDeviceClassName());
final TextView tvAddress = (TextView) lt.findViewById(R.id.deviceAddress); tvBondingState.setText(device.getBluetoothDeviceBondState());
final TextView tvClass = (TextView) lt.findViewById(R.id.deviceClass);
final TextView tvBondingState = (TextView) lt.findViewById(R.id.deviceBondingState);
tvName.setText(device.getName()); adapter.addView(lt);
tvAddress.setText(device.getAddress()); }
tvClass.setText(device.getBluetoothDeviceClassName());
tvBondingState.setText(device.getBluetoothDeviceBondState());
adapter.addView(lt); private void appendHeader(final MergeAdapter adapter, final String title) {
} final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_header, null);
final TextView tvTitle = (TextView) lt.findViewById(R.id.title);
tvTitle.setText(title);
private void appendHeader(final MergeAdapter adapter, final String title){ adapter.addView(lt);
final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_header, null); }
final TextView tvTitle = (TextView) lt.findViewById(R.id.title);
tvTitle.setText(title);
adapter.addView(lt); private void appendIBeaconInfo(final MergeAdapter adapter, final IBeaconManufacturerData iBeaconData) {
} final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_ibeacon_details, null);
final TextView tvCompanyId = (TextView) lt.findViewById(R.id.companyId);
final TextView tvAdvert = (TextView) lt.findViewById(R.id.advertisement);
final TextView tvUUID = (TextView) lt.findViewById(R.id.uuid);
final TextView tvMajor = (TextView) lt.findViewById(R.id.major);
final TextView tvMinor = (TextView) lt.findViewById(R.id.minor);
final TextView tvTxPower = (TextView) lt.findViewById(R.id.txpower);
private void appendIBeaconInfo(final MergeAdapter adapter, final IBeaconManufacturerData iBeaconData){ tvCompanyId.setText(
final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_ibeacon_details, null); CompanyIdentifierResolver.getCompanyName(iBeaconData.getCompanyIdentifier(), getString(R.string.unknown))
final TextView tvCompanyId = (TextView) lt.findViewById(R.id.companyId); + " (" + hexEncode(iBeaconData.getCompanyIdentifier()) + ")");
final TextView tvAdvert = (TextView) lt.findViewById(R.id.advertisement); tvAdvert.setText(iBeaconData.getIBeaconAdvertisement() + " (" + hexEncode(iBeaconData.getIBeaconAdvertisement()) + ")");
final TextView tvUUID = (TextView) lt.findViewById(R.id.uuid); tvUUID.setText(iBeaconData.getUUID());
final TextView tvMajor = (TextView) lt.findViewById(R.id.major); tvMajor.setText(iBeaconData.getMajor() + " (" + hexEncode(iBeaconData.getMajor()) + ")");
final TextView tvMinor = (TextView) lt.findViewById(R.id.minor); tvMinor.setText(iBeaconData.getMinor() + " (" + hexEncode(iBeaconData.getMinor()) + ")");
final TextView tvTxPower = (TextView) lt.findViewById(R.id.txpower); tvTxPower.setText(iBeaconData.getCalibratedTxPower() + " (" + hexEncode(iBeaconData.getCalibratedTxPower()) + ")");
tvCompanyId.setText( adapter.addView(lt);
CompanyIdentifierResolver.getCompanyName(iBeaconData.getCompanyIdentifier(), getString(R.string.unknown)) }
+ " (" + hexEncode(iBeaconData.getCompanyIdentifier()) + ")");
tvAdvert.setText(iBeaconData.getIBeaconAdvertisement() + " (" + hexEncode( iBeaconData.getIBeaconAdvertisement() ) + ")");
tvUUID.setText(iBeaconData.getUUID());
tvMajor.setText(iBeaconData.getMajor() + " (" + hexEncode( iBeaconData.getMajor() ) + ")");
tvMinor.setText(iBeaconData.getMinor() + " (" + hexEncode( iBeaconData.getMinor() ) + ")");
tvTxPower.setText(iBeaconData.getCalibratedTxPower() + " (" + hexEncode( iBeaconData.getCalibratedTxPower() ) + ")");
adapter.addView(lt); private void appendRssiInfo(final MergeAdapter adapter, final BluetoothLeDevice device) {
} final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_rssi_info, null);
final TextView tvFirstTimestamp = (TextView) lt.findViewById(R.id.firstTimestamp);
final TextView tvFirstRssi = (TextView) lt.findViewById(R.id.firstRssi);
final TextView tvLastTimestamp = (TextView) lt.findViewById(R.id.lastTimestamp);
final TextView tvLastRssi = (TextView) lt.findViewById(R.id.lastRssi);
final TextView tvRunningAverageRssi = (TextView) lt.findViewById(R.id.runningAverageRssi);
private void appendRssiInfo(final MergeAdapter adapter, final BluetoothLeDevice device){ tvFirstTimestamp.setText(formatTime(device.getFirstTimestamp()));
final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_rssi_info, null); tvFirstRssi.setText(formatRssi(device.getFirstRssi()));
final TextView tvFirstTimestamp = (TextView) lt.findViewById(R.id.firstTimestamp); tvLastTimestamp.setText(formatTime(device.getTimestamp()));
final TextView tvFirstRssi = (TextView) lt.findViewById(R.id.firstRssi); tvLastRssi.setText(formatRssi(device.getRssi()));
final TextView tvLastTimestamp = (TextView) lt.findViewById(R.id.lastTimestamp); tvRunningAverageRssi.setText(formatRssi(device.getRunningAverageRssi()));
final TextView tvLastRssi = (TextView) lt.findViewById(R.id.lastRssi);
final TextView tvRunningAverageRssi = (TextView) lt.findViewById(R.id.runningAverageRssi);
tvFirstTimestamp.setText(formatTime(device.getFirstTimestamp())); adapter.addView(lt);
tvFirstRssi.setText(formatRssi(device.getFirstRssi())); }
tvLastTimestamp.setText(formatTime(device.getTimestamp()));
tvLastRssi.setText(formatRssi(device.getRssi()));
tvRunningAverageRssi.setText(formatRssi(device.getRunningAverageRssi()));
adapter.addView(lt); private void appendSimpleText(final MergeAdapter adapter, final byte[] data) {
} appendSimpleText(adapter, ByteUtils.byteArrayToHexString(data));
}
private void appendSimpleText(final MergeAdapter adapter, final byte[] data){ private void appendSimpleText(final MergeAdapter adapter, final String data) {
appendSimpleText(adapter, ByteUtils.byteArrayToHexString(data)); final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_textview, null);
} final TextView tvData = (TextView) lt.findViewById(R.id.data);
private void appendSimpleText(final MergeAdapter adapter, final String data){ tvData.setText(data);
final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_textview, null);
final TextView tvData = (TextView) lt.findViewById(R.id.data);
tvData.setText(data); adapter.addView(lt);
}
adapter.addView(lt);
}
private String formatRssi(final double rssi){ private String formatRssi(final double rssi) {
return getString(R.string.formatter_db, String.valueOf(rssi)); return getString(R.string.formatter_db, String.valueOf(rssi));
} }
private String formatRssi(final int rssi){ private String formatRssi(final int rssi) {
return getString(R.string.formatter_db, String.valueOf(rssi)); return getString(R.string.formatter_db, String.valueOf(rssi));
} }
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details); setContentView(R.layout.activity_details);
ButterKnife.inject(this); ButterKnife.inject(this);
mDevice = getIntent().getParcelableExtra(EXTRA_DEVICE); mDevice = getIntent().getParcelableExtra(EXTRA_DEVICE);
pupulateDetails(mDevice); pupulateDetails(mDevice);
} }
@Override @Override
public boolean onCreateOptionsMenu(final Menu menu) { public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.details, menu); getMenuInflater().inflate(R.menu.details, menu);
return true; return true;
} }
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_connect: case R.id.menu_connect:
final Intent intent = new Intent(this, DeviceControlActivity.class); final Intent intent = new Intent(this, DeviceControlActivity.class);
intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, mDevice); intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, mDevice);
startActivity(intent); startActivity(intent);
break; break;
} }
return true; return true;
} }
private void pupulateDetails(final BluetoothLeDevice device) { private void pupulateDetails(final BluetoothLeDevice device) {
final MergeAdapter adapter = new MergeAdapter(); final MergeAdapter adapter = new MergeAdapter();
if(device == null){ if (device == null) {
appendHeader(adapter, getString(R.string.header_device_info)); appendHeader(adapter, getString(R.string.header_device_info));
appendSimpleText(adapter, getString(R.string.invalid_device_data)); appendSimpleText(adapter, getString(R.string.invalid_device_data));
} else { } else {
appendHeader(adapter, getString(R.string.header_device_info)); appendHeader(adapter, getString(R.string.header_device_info));
appendDeviceInfo(adapter, device); appendDeviceInfo(adapter, device);
appendHeader(adapter, getString(R.string.header_rssi_info)); appendHeader(adapter, getString(R.string.header_rssi_info));
appendRssiInfo(adapter, device); appendRssiInfo(adapter, device);
appendHeader(adapter, getString(R.string.header_scan_record)); appendHeader(adapter, getString(R.string.header_scan_record));
appendSimpleText(adapter, device.getScanRecord()); appendSimpleText(adapter, device.getScanRecord());
final Collection<AdRecord> adRecords = device.getAdRecordStore().getRecordsAsCollection(); final Collection<AdRecord> adRecords = device.getAdRecordStore().getRecordsAsCollection();
if(adRecords.size() > 0){ if (adRecords.size() > 0) {
appendHeader(adapter, getString(R.string.header_raw_ad_records)); appendHeader(adapter, getString(R.string.header_raw_ad_records));
for(final AdRecord record : adRecords){ for (final AdRecord record : adRecords) {
appendAdRecordView( appendAdRecordView(
adapter, adapter,
"#" + record.getType() + " " + record.getHumanReadableType(), "#" + record.getType() + " " + record.getHumanReadableType(),
record); record);
} }
} }
final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device); final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device);
if(isIBeacon){ if (isIBeacon) {
final IBeaconManufacturerData iBeaconData = new IBeaconManufacturerData(device); final IBeaconManufacturerData iBeaconData = new IBeaconManufacturerData(device);
appendHeader(adapter, getString(R.string.header_ibeacon_data)); appendHeader(adapter, getString(R.string.header_ibeacon_data));
appendIBeaconInfo(adapter, iBeaconData); appendIBeaconInfo(adapter, iBeaconData);
} }
} }
getListView().setAdapter(adapter); getListView().setAdapter(adapter);
} }
private static String formatTime(final long time){ private static String formatTime(final long time) {
return TimeFormatter.getIsoDateTime(time); return TimeFormatter.getIsoDateTime(time);
} }
private static String hexEncode(final int integer){ private static String hexEncode(final int integer) {
return "0x" + Integer.toHexString(integer).toUpperCase(Locale.US); return "0x" + Integer.toHexString(integer).toUpperCase(Locale.US);
} }
} }
@@ -1,12 +1,5 @@
package uk.co.alt236.btlescan.activities; package uk.co.alt236.btlescan.activities;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.btlescan.R;
import uk.co.alt236.btlescan.adapters.LeDeviceListAdapter;
import uk.co.alt236.btlescan.containers.BluetoothLeDeviceStore;
import uk.co.alt236.btlescan.util.BluetoothLeScanner;
import uk.co.alt236.btlescan.util.BluetoothUtils;
import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ListActivity; import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
@@ -22,174 +15,186 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.InjectView; import butterknife.InjectView;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.btlescan.R;
import uk.co.alt236.btlescan.adapters.LeDeviceListAdapter;
import uk.co.alt236.btlescan.containers.BluetoothLeDeviceStore;
import uk.co.alt236.btlescan.util.BluetoothLeScanner;
import uk.co.alt236.btlescan.util.BluetoothUtils;
import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor;
public class MainActivity extends ListActivity { public class MainActivity extends ListActivity {
@InjectView(R.id.tvBluetoothLe) TextView mTvBluetoothLeStatus; @InjectView(R.id.tvBluetoothLe)
@InjectView(R.id.tvBluetoothStatus) TextView mTvBluetoothStatus; TextView mTvBluetoothLeStatus;
@InjectView(R.id.tvItemCount) TextView mTvItemCount; @InjectView(R.id.tvBluetoothStatus)
TextView mTvBluetoothStatus;
@InjectView(R.id.tvItemCount)
TextView mTvItemCount;
private BluetoothUtils mBluetoothUtils; private BluetoothUtils mBluetoothUtils;
private BluetoothLeScanner mScanner; private BluetoothLeScanner mScanner;
private LeDeviceListAdapter mLeDeviceListAdapter; private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothLeDeviceStore mDeviceStore; private BluetoothLeDeviceStore mDeviceStore;
private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override @Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
final BluetoothLeDevice deviceLe = new BluetoothLeDevice(device, rssi, scanRecord, System.currentTimeMillis()); final BluetoothLeDevice deviceLe = new BluetoothLeDevice(device, rssi, scanRecord, System.currentTimeMillis());
mDeviceStore.addDevice(deviceLe); mDeviceStore.addDevice(deviceLe);
final EasyObjectCursor<BluetoothLeDevice> c = mDeviceStore.getDeviceCursor(); final EasyObjectCursor<BluetoothLeDevice> c = mDeviceStore.getDeviceCursor();
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
mLeDeviceListAdapter.swapCursor(c); mLeDeviceListAdapter.swapCursor(c);
updateItemCount(mLeDeviceListAdapter.getCount()); updateItemCount(mLeDeviceListAdapter.getCount());
} }
}); });
} }
}; };
private void updateItemCount(final int count){ private void displayAboutDialog() {
mTvItemCount.setText( // REALLY REALLY LAZY LINKIFIED DIALOG
getString( final int paddingSizeDp = 5;
R.string.formatter_item_count, final float scale = getResources().getDisplayMetrics().density;
String.valueOf(count))); final int dpAsPixels = (int) (paddingSizeDp * scale + 0.5f);
}
private void displayAboutDialog(){ final TextView textView = new TextView(this);
// REALLY REALLY LAZY LINKIFIED DIALOG final SpannableString text = new SpannableString(getString(R.string.about_dialog_text));
final int paddingSizeDp = 5;
final float scale = getResources().getDisplayMetrics().density;
final int dpAsPixels = (int) (paddingSizeDp * scale + 0.5f);
final TextView textView=new TextView(this); textView.setText(text);
final SpannableString text = new SpannableString(getString(R.string.about_dialog_text)); textView.setAutoLinkMask(RESULT_OK);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setPadding(dpAsPixels, dpAsPixels, dpAsPixels, dpAsPixels);
textView.setText(text); Linkify.addLinks(text, Linkify.ALL);
textView.setAutoLinkMask(RESULT_OK); new AlertDialog.Builder(this)
textView.setMovementMethod(LinkMovementMethod.getInstance()); .setTitle(R.string.menu_about)
textView.setPadding(dpAsPixels, dpAsPixels, dpAsPixels, dpAsPixels); .setCancelable(false)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int id) {
}
})
.setView(textView)
.show();
}
Linkify.addLinks(text, Linkify.ALL); @Override
new AlertDialog.Builder(this) protected void onCreate(final Bundle savedInstanceState) {
.setTitle(R.string.menu_about) super.onCreate(savedInstanceState);
.setCancelable(false) setContentView(R.layout.activity_main);
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { ButterKnife.inject(this);
public void onClick(final DialogInterface dialog, final int id) {}
})
.setView(textView)
.show();
}
@Override mDeviceStore = new BluetoothLeDeviceStore();
protected void onCreate(final Bundle savedInstanceState) { mBluetoothUtils = new BluetoothUtils(this);
super.onCreate(savedInstanceState); mScanner = new BluetoothLeScanner(mLeScanCallback, mBluetoothUtils);
setContentView(R.layout.activity_main); updateItemCount(0);
ButterKnife.inject(this); }
mDeviceStore = new BluetoothLeDeviceStore(); @Override
mBluetoothUtils = new BluetoothUtils(this); public boolean onCreateOptionsMenu(final Menu menu) {
mScanner = new BluetoothLeScanner(mLeScanCallback, mBluetoothUtils); getMenuInflater().inflate(R.menu.main, menu);
updateItemCount(0); if (!mScanner.isScanning()) {
} menu.findItem(R.id.menu_stop).setVisible(false);
menu.findItem(R.id.menu_scan).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(null);
} else {
menu.findItem(R.id.menu_stop).setVisible(true);
menu.findItem(R.id.menu_scan).setVisible(false);
menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_progress_indeterminate);
}
@Override if (getListView().getCount() > 0) {
public boolean onCreateOptionsMenu(final Menu menu) { menu.findItem(R.id.menu_share).setVisible(true);
getMenuInflater().inflate(R.menu.main, menu); } else {
if (!mScanner.isScanning()) { menu.findItem(R.id.menu_share).setVisible(false);
menu.findItem(R.id.menu_stop).setVisible(false); }
menu.findItem(R.id.menu_scan).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(null);
} else {
menu.findItem(R.id.menu_stop).setVisible(true);
menu.findItem(R.id.menu_scan).setVisible(false);
menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_progress_indeterminate);
}
if(getListView().getCount() > 0){ return true;
menu.findItem(R.id.menu_share).setVisible(true); }
} else {
menu.findItem(R.id.menu_share).setVisible(false);
}
return true; @Override
} protected void onListItemClick(final ListView l, final View v, final int position, final long id) {
final BluetoothLeDevice device = mLeDeviceListAdapter.getItem(position);
if (device == null) return;
@Override final Intent intent = new Intent(this, DeviceDetailsActivity.class);
protected void onListItemClick(final ListView l, final View v, final int position, final long id) { intent.putExtra(DeviceDetailsActivity.EXTRA_DEVICE, device);
final BluetoothLeDevice device = mLeDeviceListAdapter.getItem(position);
if (device == null) return;
final Intent intent = new Intent(this, DeviceDetailsActivity.class); startActivity(intent);
intent.putExtra(DeviceDetailsActivity.EXTRA_DEVICE, device); }
startActivity(intent); @Override
} public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_scan:
startScan();
break;
case R.id.menu_stop:
mScanner.scanLeDevice(-1, false);
invalidateOptionsMenu();
break;
case R.id.menu_about:
displayAboutDialog();
break;
case R.id.menu_share:
mDeviceStore.shareDataAsEmail(this);
}
return true;
}
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { protected void onPause() {
switch (item.getItemId()) { super.onPause();
case R.id.menu_scan: mScanner.scanLeDevice(-1, false);
startScan(); }
break;
case R.id.menu_stop:
mScanner.scanLeDevice(-1, false);
invalidateOptionsMenu();
break;
case R.id.menu_about:
displayAboutDialog();
break;
case R.id.menu_share:
mDeviceStore.shareDataAsEmail(this);
}
return true;
}
@Override @Override
protected void onPause() { public void onResume() {
super.onPause(); super.onResume();
mScanner.scanLeDevice(-1, false); final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn();
} final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported();
@Override if (mIsBluetoothOn) {
public void onResume(){ mTvBluetoothStatus.setText(R.string.on);
super.onResume(); } else {
final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn(); mTvBluetoothStatus.setText(R.string.off);
final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported(); }
if(mIsBluetoothOn){ if (mIsBluetoothLePresent) {
mTvBluetoothStatus.setText(R.string.on); mTvBluetoothLeStatus.setText(R.string.supported);
} else { } else {
mTvBluetoothStatus.setText(R.string.off); mTvBluetoothLeStatus.setText(R.string.not_supported);
} }
if(mIsBluetoothLePresent){ invalidateOptionsMenu();
mTvBluetoothLeStatus.setText(R.string.supported); }
} else {
mTvBluetoothLeStatus.setText(R.string.not_supported);
}
invalidateOptionsMenu(); private void startScan() {
} final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn();
final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported();
mDeviceStore.clear();
updateItemCount(0);
private void startScan(){ mLeDeviceListAdapter = new LeDeviceListAdapter(this, mDeviceStore.getDeviceCursor());
final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn(); setListAdapter(mLeDeviceListAdapter);
final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported();
mDeviceStore.clear();
updateItemCount(0);
mLeDeviceListAdapter = new LeDeviceListAdapter(this, mDeviceStore.getDeviceCursor()); mBluetoothUtils.askUserToEnableBluetoothIfNeeded();
setListAdapter(mLeDeviceListAdapter); if (mIsBluetoothOn && mIsBluetoothLePresent) {
mScanner.scanLeDevice(-1, true);
invalidateOptionsMenu();
}
}
mBluetoothUtils.askUserToEnableBluetoothIfNeeded(); private void updateItemCount(final int count) {
if(mIsBluetoothOn && mIsBluetoothLePresent){ mTvItemCount.setText(
mScanner.scanLeDevice(-1, true); getString(
invalidateOptionsMenu(); R.string.formatter_item_count,
} String.valueOf(count)));
} }
} }
@@ -1,11 +1,5 @@
package uk.co.alt236.btlescan.adapters; package uk.co.alt236.btlescan.adapters;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.bluetoothlelib.device.IBeaconDevice;
import uk.co.alt236.bluetoothlelib.util.IBeaconUtils;
import uk.co.alt236.btlescan.R;
import uk.co.alt236.btlescan.util.Constants;
import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor;
import android.app.Activity; import android.app.Activity;
import android.support.v4.widget.SimpleCursorAdapter; import android.support.v4.widget.SimpleCursorAdapter;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -13,111 +7,119 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.bluetoothlelib.device.IBeaconDevice;
import uk.co.alt236.bluetoothlelib.util.IBeaconUtils;
import uk.co.alt236.btlescan.R;
import uk.co.alt236.btlescan.util.Constants;
import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor;
// Adapter for holding devices found through scanning. // Adapter for holding devices found through scanning.
public class LeDeviceListAdapter extends SimpleCursorAdapter { public class LeDeviceListAdapter extends SimpleCursorAdapter {
private final LayoutInflater mInflator; private final LayoutInflater mInflator;
private final Activity mActivity; private final Activity mActivity;
public LeDeviceListAdapter(final Activity activity, final EasyObjectCursor<BluetoothLeDevice> cursor) { public LeDeviceListAdapter(final Activity activity, final EasyObjectCursor<BluetoothLeDevice> cursor) {
super(activity, R.layout.list_item_device, cursor, new String[0], new int[0], 0); super(activity, R.layout.list_item_device, cursor, new String[0], new int[0], 0);
mInflator = activity.getLayoutInflater(); mInflator = activity.getLayoutInflater();
mActivity = activity; mActivity = activity;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public EasyObjectCursor<BluetoothLeDevice> getCursor(){ public EasyObjectCursor<BluetoothLeDevice> getCursor() {
return ((EasyObjectCursor<BluetoothLeDevice>) super.getCursor()); return ((EasyObjectCursor<BluetoothLeDevice>) super.getCursor());
} }
@Override @Override
public BluetoothLeDevice getItem(final int i){ public BluetoothLeDevice getItem(final int i) {
return getCursor().getItem(i); return getCursor().getItem(i);
} }
@Override @Override
public long getItemId(final int i) { public long getItemId(final int i) {
return i; return i;
} }
@Override @Override
public View getView(final int i, View view, final ViewGroup viewGroup) { public View getView(final int i, View view, final ViewGroup viewGroup) {
final ViewHolder viewHolder; final ViewHolder viewHolder;
// General ListView optimization code. // General ListView optimization code.
if (view == null) { if (view == null) {
view = mInflator.inflate(R.layout.list_item_device, null); view = mInflator.inflate(R.layout.list_item_device, null);
viewHolder = new ViewHolder(); viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address); viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name); viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi); viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi);
viewHolder.deviceIcon = (ImageView) view.findViewById(R.id.device_icon); viewHolder.deviceIcon = (ImageView) view.findViewById(R.id.device_icon);
viewHolder.deviceLastUpdated = (TextView) view.findViewById(R.id.device_last_update); viewHolder.deviceLastUpdated = (TextView) view.findViewById(R.id.device_last_update);
viewHolder.ibeaconMajor = (TextView) view.findViewById(R.id.ibeacon_major); viewHolder.ibeaconMajor = (TextView) view.findViewById(R.id.ibeacon_major);
viewHolder.ibeaconMinor = (TextView) view.findViewById(R.id.ibeacon_minor); viewHolder.ibeaconMinor = (TextView) view.findViewById(R.id.ibeacon_minor);
viewHolder.ibeaconDistance = (TextView) view.findViewById(R.id.ibeacon_distance); viewHolder.ibeaconDistance = (TextView) view.findViewById(R.id.ibeacon_distance);
viewHolder.ibeaconUUID = (TextView) view.findViewById(R.id.ibeacon_uuid); viewHolder.ibeaconUUID = (TextView) view.findViewById(R.id.ibeacon_uuid);
viewHolder.ibeaconTxPower = (TextView) view.findViewById(R.id.ibeacon_tx_power); viewHolder.ibeaconTxPower = (TextView) view.findViewById(R.id.ibeacon_tx_power);
viewHolder.ibeaconSection = view.findViewById(R.id.ibeacon_section); viewHolder.ibeaconSection = view.findViewById(R.id.ibeacon_section);
viewHolder.ibeaconDistanceDescriptor = (TextView) view.findViewById(R.id.ibeacon_distance_descriptor); viewHolder.ibeaconDistanceDescriptor = (TextView) view.findViewById(R.id.ibeacon_distance_descriptor);
view.setTag(viewHolder); view.setTag(viewHolder);
} else { } else {
viewHolder = (ViewHolder) view.getTag(); viewHolder = (ViewHolder) view.getTag();
} }
final BluetoothLeDevice device = getCursor().getItem(i); final BluetoothLeDevice device = getCursor().getItem(i);
final String deviceName = device.getName(); final String deviceName = device.getName();
final double rssi = device.getRssi(); final double rssi = device.getRssi();
if (deviceName != null && deviceName.length() > 0){ if (deviceName != null && deviceName.length() > 0) {
viewHolder.deviceName.setText(deviceName); viewHolder.deviceName.setText(deviceName);
} else{ } else {
viewHolder.deviceName.setText(R.string.unknown_device); viewHolder.deviceName.setText(R.string.unknown_device);
} }
if (IBeaconUtils.isThisAnIBeacon(device)){ if (IBeaconUtils.isThisAnIBeacon(device)) {
final IBeaconDevice iBeacon = new IBeaconDevice(device); final IBeaconDevice iBeacon = new IBeaconDevice(device);
final String accuracy = Constants.DOUBLE_TWO_DIGIT_ACCURACY.format(iBeacon.getAccuracy()); final String accuracy = Constants.DOUBLE_TWO_DIGIT_ACCURACY.format(iBeacon.getAccuracy());
viewHolder.deviceIcon.setImageResource(R.drawable.ic_device_ibeacon); viewHolder.deviceIcon.setImageResource(R.drawable.ic_device_ibeacon);
viewHolder.ibeaconSection.setVisibility(View.VISIBLE); viewHolder.ibeaconSection.setVisibility(View.VISIBLE);
viewHolder.ibeaconMajor.setText(String.valueOf(iBeacon.getMajor())); viewHolder.ibeaconMajor.setText(String.valueOf(iBeacon.getMajor()));
viewHolder.ibeaconMinor.setText(String.valueOf(iBeacon.getMinor())); viewHolder.ibeaconMinor.setText(String.valueOf(iBeacon.getMinor()));
viewHolder.ibeaconTxPower.setText(String.valueOf(iBeacon.getCalibratedTxPower())); viewHolder.ibeaconTxPower.setText(String.valueOf(iBeacon.getCalibratedTxPower()));
viewHolder.ibeaconUUID.setText(iBeacon.getUUID()); viewHolder.ibeaconUUID.setText(iBeacon.getUUID());
viewHolder.ibeaconDistance.setText( viewHolder.ibeaconDistance.setText(
mActivity.getString(R.string.formatter_meters, accuracy)); mActivity.getString(R.string.formatter_meters, accuracy));
viewHolder.ibeaconDistanceDescriptor.setText(iBeacon.getDistanceDescriptor().toString()); viewHolder.ibeaconDistanceDescriptor.setText(iBeacon.getDistanceDescriptor().toString());
} else { } else {
viewHolder.deviceIcon.setImageResource(R.drawable.ic_bluetooth); viewHolder.deviceIcon.setImageResource(R.drawable.ic_bluetooth);
viewHolder.ibeaconSection.setVisibility(View.GONE); viewHolder.ibeaconSection.setVisibility(View.GONE);
} }
final String rssiString = final String rssiString =
mActivity.getString(R.string.formatter_db, String.valueOf(rssi)); mActivity.getString(R.string.formatter_db, String.valueOf(rssi));
final String runningAverageRssiString = final String runningAverageRssiString =
mActivity.getString(R.string.formatter_db, String.valueOf(device.getRunningAverageRssi())); mActivity.getString(R.string.formatter_db, String.valueOf(device.getRunningAverageRssi()));
viewHolder.deviceLastUpdated.setText( viewHolder.deviceLastUpdated.setText(
android.text.format.DateFormat.format( android.text.format.DateFormat.format(
Constants.TIME_FORMAT, new java.util.Date(device.getTimestamp()))); Constants.TIME_FORMAT, new java.util.Date(device.getTimestamp())));
viewHolder.deviceAddress.setText(device.getAddress()); viewHolder.deviceAddress.setText(device.getAddress());
viewHolder.deviceRssi.setText(rssiString + " / " + runningAverageRssiString); viewHolder.deviceRssi.setText(rssiString + " / " + runningAverageRssiString);
return view; return view;
} }
static class ViewHolder { static class ViewHolder {
TextView deviceName; TextView deviceName;
TextView deviceAddress; TextView deviceAddress;
TextView deviceRssi; TextView deviceRssi;
TextView ibeaconUUID; TextView ibeaconUUID;
TextView ibeaconMajor; TextView ibeaconMajor;
TextView ibeaconMinor; TextView ibeaconMinor;
TextView ibeaconTxPower; TextView ibeaconTxPower;
TextView ibeaconDistance; TextView ibeaconDistance;
TextView ibeaconDistanceDescriptor; TextView ibeaconDistanceDescriptor;
TextView deviceLastUpdated; TextView deviceLastUpdated;
View ibeaconSection; View ibeaconSection;
ImageView deviceIcon; ImageView deviceIcon;
} }
} }
@@ -1,5 +1,9 @@
package uk.co.alt236.btlescan.containers; package uk.co.alt236.btlescan.containers;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
@@ -19,158 +23,133 @@ import uk.co.alt236.btlescan.R;
import uk.co.alt236.btlescan.util.CsvWriterHelper; import uk.co.alt236.btlescan.util.CsvWriterHelper;
import uk.co.alt236.btlescan.util.TimeFormatter; import uk.co.alt236.btlescan.util.TimeFormatter;
import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor; import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
public class BluetoothLeDeviceStore { public class BluetoothLeDeviceStore {
private final Map<String, BluetoothLeDevice> mDeviceMap; private final Map<String, BluetoothLeDevice> mDeviceMap;
public BluetoothLeDeviceStore(){ public BluetoothLeDeviceStore() {
mDeviceMap = new HashMap<String, BluetoothLeDevice>(); mDeviceMap = new HashMap<String, BluetoothLeDevice>();
} }
public void addDevice(final BluetoothLeDevice device){ public void addDevice(final BluetoothLeDevice device) {
if(mDeviceMap.containsKey(device.getAddress())){ if (mDeviceMap.containsKey(device.getAddress())) {
mDeviceMap.get(device.getAddress()).updateRssiReading(device.getTimestamp(), device.getRssi()); mDeviceMap.get(device.getAddress()).updateRssiReading(device.getTimestamp(), device.getRssi());
} else { } else {
mDeviceMap.put(device.getAddress(), device); mDeviceMap.put(device.getAddress(), device);
} }
} }
public void clear(){ public void clear() {
mDeviceMap.clear(); mDeviceMap.clear();
} }
public EasyObjectCursor<BluetoothLeDevice> getDeviceCursor() {
return new EasyObjectCursor<BluetoothLeDevice>(
BluetoothLeDevice.class,
getDeviceList(),
"address");
}
private static FileWriter generateFile(final File file, final String contents){ public List<BluetoothLeDevice> getDeviceList() {
FileWriter writer = null; final List<BluetoothLeDevice> methodResult = new ArrayList<BluetoothLeDevice>(mDeviceMap.values());
try {
writer = new FileWriter(file);
writer.append(contents);
writer.flush();
} catch (final IOException e) { Collections.sort(methodResult, new Comparator<BluetoothLeDevice>() {
e.printStackTrace();
}finally{
try {
writer.close();
} catch (final IOException e) {
e.printStackTrace();
}
}
return writer;
}
public EasyObjectCursor<BluetoothLeDevice> getDeviceCursor(){ @Override
return new EasyObjectCursor<BluetoothLeDevice>( public int compare(final BluetoothLeDevice arg0, final BluetoothLeDevice arg1) {
BluetoothLeDevice.class, return arg0.getAddress().compareToIgnoreCase(arg1.getAddress());
getDeviceList(), }
"address"); });
}
public List<BluetoothLeDevice> getDeviceList(){ return methodResult;
final List<BluetoothLeDevice> methodResult = new ArrayList<BluetoothLeDevice>(mDeviceMap.values()); }
Collections.sort(methodResult, new Comparator<BluetoothLeDevice>() { private String getListAsCsv() {
final List<BluetoothLeDevice> list = getDeviceList();
final StringBuilder sb = new StringBuilder();
sb.append(CsvWriterHelper.addStuff("mac"));
sb.append(CsvWriterHelper.addStuff("name"));
sb.append(CsvWriterHelper.addStuff("firstTimestamp"));
sb.append(CsvWriterHelper.addStuff("firstRssi"));
sb.append(CsvWriterHelper.addStuff("currentTimestamp"));
sb.append(CsvWriterHelper.addStuff("currentRssi"));
sb.append(CsvWriterHelper.addStuff("adRecord"));
sb.append(CsvWriterHelper.addStuff("iBeacon"));
sb.append(CsvWriterHelper.addStuff("uuid"));
sb.append(CsvWriterHelper.addStuff("major"));
sb.append(CsvWriterHelper.addStuff("minor"));
sb.append(CsvWriterHelper.addStuff("txPower"));
sb.append(CsvWriterHelper.addStuff("distance"));
sb.append(CsvWriterHelper.addStuff("accuracy"));
sb.append('\n');
@Override for (final BluetoothLeDevice device : list) {
public int compare(final BluetoothLeDevice arg0, final BluetoothLeDevice arg1) { sb.append(CsvWriterHelper.addStuff(device.getAddress()));
return arg0.getAddress().compareToIgnoreCase(arg1.getAddress()); sb.append(CsvWriterHelper.addStuff(device.getName()));
} sb.append(CsvWriterHelper.addStuff(TimeFormatter.getIsoDateTime(device.getFirstTimestamp())));
}); sb.append(CsvWriterHelper.addStuff(device.getFirstRssi()));
sb.append(CsvWriterHelper.addStuff(TimeFormatter.getIsoDateTime(device.getTimestamp())));
sb.append(CsvWriterHelper.addStuff(device.getRssi()));
sb.append(CsvWriterHelper.addStuff(ByteUtils.byteArrayToHexString(device.getScanRecord())));
final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device);
final String uuid;
final String minor;
final String major;
final String txPower;
final String distance;
final String accuracy;
return methodResult; if (isIBeacon) {
} final IBeaconDevice beacon = new IBeaconDevice(device);
uuid = String.valueOf(beacon.getUUID());
minor = String.valueOf(beacon.getMinor());
major = String.valueOf(beacon.getMajor());
txPower = String.valueOf(beacon.getCalibratedTxPower());
distance = beacon.getDistanceDescriptor().toString().toLowerCase(Locale.US);
accuracy = String.valueOf(beacon.getAccuracy());
} else {
uuid = "";
minor = "";
major = "";
txPower = "";
distance = "";
accuracy = "";
}
sb.append(CsvWriterHelper.addStuff(isIBeacon));
sb.append(CsvWriterHelper.addStuff(uuid));
sb.append(CsvWriterHelper.addStuff(minor));
sb.append(CsvWriterHelper.addStuff(major));
sb.append(CsvWriterHelper.addStuff(txPower));
sb.append(CsvWriterHelper.addStuff(distance));
sb.append(CsvWriterHelper.addStuff(accuracy));
private String getListAsCsv(){ sb.append('\n');
final List<BluetoothLeDevice> list = getDeviceList(); }
final StringBuilder sb = new StringBuilder();
sb.append(CsvWriterHelper.addStuff("mac"));
sb.append(CsvWriterHelper.addStuff("name"));
sb.append(CsvWriterHelper.addStuff("firstTimestamp"));
sb.append(CsvWriterHelper.addStuff("firstRssi"));
sb.append(CsvWriterHelper.addStuff("currentTimestamp"));
sb.append(CsvWriterHelper.addStuff("currentRssi"));
sb.append(CsvWriterHelper.addStuff("adRecord"));
sb.append(CsvWriterHelper.addStuff("iBeacon"));
sb.append(CsvWriterHelper.addStuff("uuid"));
sb.append(CsvWriterHelper.addStuff("major"));
sb.append(CsvWriterHelper.addStuff("minor"));
sb.append(CsvWriterHelper.addStuff("txPower"));
sb.append(CsvWriterHelper.addStuff("distance"));
sb.append(CsvWriterHelper.addStuff("accuracy"));
sb.append('\n');
for(final BluetoothLeDevice device : list){ return sb.toString();
sb.append(CsvWriterHelper.addStuff(device.getAddress())); }
sb.append(CsvWriterHelper.addStuff(device.getName()));
sb.append(CsvWriterHelper.addStuff(TimeFormatter.getIsoDateTime(device.getFirstTimestamp())));
sb.append(CsvWriterHelper.addStuff(device.getFirstRssi()));
sb.append(CsvWriterHelper.addStuff(TimeFormatter.getIsoDateTime(device.getTimestamp())));
sb.append(CsvWriterHelper.addStuff(device.getRssi()));
sb.append(CsvWriterHelper.addStuff(ByteUtils.byteArrayToHexString(device.getScanRecord())));
final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device);
final String uuid;
final String minor;
final String major;
final String txPower;
final String distance;
final String accuracy;
if(isIBeacon){ public void shareDataAsEmail(final Context context) {
final IBeaconDevice beacon = new IBeaconDevice(device); final long timeInMillis = System.currentTimeMillis();
uuid = String.valueOf(beacon.getUUID());
minor = String.valueOf(beacon.getMinor());
major = String.valueOf(beacon.getMajor());
txPower = String.valueOf(beacon.getCalibratedTxPower());
distance = beacon.getDistanceDescriptor().toString().toLowerCase(Locale.US);
accuracy = String.valueOf(beacon.getAccuracy());
} else {
uuid = "";
minor = "";
major = "";
txPower = "";
distance = "";
accuracy = "";
}
sb.append(CsvWriterHelper.addStuff(isIBeacon)); final String to = null;
sb.append(CsvWriterHelper.addStuff(uuid));
sb.append(CsvWriterHelper.addStuff(minor));
sb.append(CsvWriterHelper.addStuff(major));
sb.append(CsvWriterHelper.addStuff(txPower));
sb.append(CsvWriterHelper.addStuff(distance));
sb.append(CsvWriterHelper.addStuff(accuracy));
sb.append('\n');
}
return sb.toString();
}
public void shareDataAsEmail(final Context context){
final long timeInMillis = System.currentTimeMillis();
final String to = null;
final String subject = context.getString( final String subject = context.getString(
R.string.exporter_email_device_list_subject, R.string.exporter_email_device_list_subject,
TimeFormatter.getIsoDateTime(timeInMillis)); TimeFormatter.getIsoDateTime(timeInMillis));
final String message = context.getString(R.string.exporter_email_device_list_body); final String message = context.getString(R.string.exporter_email_device_list_body);
final Intent i = new Intent(Intent.ACTION_SEND); final Intent i = new Intent(Intent.ACTION_SEND);
i.setType("plain/text"); i.setType("plain/text");
try { try {
final File outputDir = context.getCacheDir(); final File outputDir = context.getCacheDir();
final File outputFile = File.createTempFile("bluetooth_le_" + timeInMillis, ".csv", outputDir); final File outputFile = File.createTempFile("bluetooth_le_" + timeInMillis, ".csv", outputDir);
outputFile.setReadable(true, false); outputFile.setReadable(true, false);
generateFile(outputFile, getListAsCsv()); generateFile(outputFile, getListAsCsv());
i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(outputFile)); i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(outputFile));
i.putExtra(Intent.EXTRA_EMAIL, new String[] { to }); i.putExtra(Intent.EXTRA_EMAIL, new String[]{to});
i.putExtra(Intent.EXTRA_SUBJECT, subject); i.putExtra(Intent.EXTRA_SUBJECT, subject);
i.putExtra(Intent.EXTRA_TEXT, message); i.putExtra(Intent.EXTRA_TEXT, message);
context.startActivity(Intent.createChooser(i, context.getString(R.string.exporter_email_device_list_picker_text))); context.startActivity(Intent.createChooser(i, context.getString(R.string.exporter_email_device_list_picker_text)));
@@ -178,5 +157,24 @@ public class BluetoothLeDeviceStore {
} catch (final IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private static FileWriter generateFile(final File file, final String contents) {
FileWriter writer = null;
try {
writer = new FileWriter(file);
writer.append(contents);
writer.flush();
} catch (final IOException e) {
e.printStackTrace();
} finally {
try {
writer.close();
} catch (final IOException e) {
e.printStackTrace();
}
}
return writer;
}
} }
@@ -16,8 +16,6 @@
package uk.co.alt236.btlescan.services; package uk.co.alt236.btlescan.services;
import java.util.List;
import android.app.Service; import android.app.Service;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
@@ -33,247 +31,243 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import java.util.List;
/** /**
* Service for managing connection and data communication with a GATT server hosted on a * Service for managing connection and data communication with a GATT server hosted on a
* given Bluetooth LE device. * given Bluetooth LE device.
*/ */
public class BluetoothLeService extends Service { public class BluetoothLeService extends Service {
private final static String TAG = BluetoothLeService.class.getSimpleName(); public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA_RAW = "com.example.bluetooth.le.EXTRA_DATA_RAW";
public final static String EXTRA_UUID_CHAR = "com.example.bluetooth.le.EXTRA_UUID_CHAR";
private final static String TAG = BluetoothLeService.class.getSimpleName();
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
private final IBinder mBinder = new LocalBinder();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
private BluetoothGatt mBluetoothGatt;
private int mConnectionState = STATE_DISCONNECTED;
// 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 onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
private BluetoothManager mBluetoothManager; @Override
private BluetoothAdapter mBluetoothAdapter; public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
private String mBluetoothDeviceAddress; if (status == BluetoothGatt.GATT_SUCCESS) {
private BluetoothGatt mBluetoothGatt; broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
private int mConnectionState = STATE_DISCONNECTED; }
}
private static final int STATE_DISCONNECTED = 0; @Override
private static final int STATE_CONNECTING = 1; public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
private static final int STATE_CONNECTED = 2; final 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());
public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; intentAction = ACTION_GATT_DISCONNECTED;
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; mConnectionState = STATE_DISCONNECTED;
public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; Log.i(TAG, "Disconnected from GATT server.");
public final static String EXTRA_DATA_RAW = "com.example.bluetooth.le.EXTRA_DATA_RAW"; broadcastUpdate(intentAction);
public final static String EXTRA_UUID_CHAR = "com.example.bluetooth.le.EXTRA_UUID_CHAR"; }
}
// Implements callback methods for GATT events that the app cares about. For example, @Override
// connection change and services discovered. public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { if (status == BluetoothGatt.GATT_SUCCESS) {
@Override broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { } else {
final String intentAction; Log.w(TAG, "onServicesDiscovered received: " + status);
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) { private void broadcastUpdate(final String action) {
intentAction = ACTION_GATT_DISCONNECTED; final Intent intent = new Intent(action);
mConnectionState = STATE_DISCONNECTED; sendBroadcast(intent);
Log.i(TAG, "Disconnected from GATT server."); }
broadcastUpdate(intentAction);
}
}
@Override private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) { final Intent intent = new Intent(action);
if (status == BluetoothGatt.GATT_SUCCESS) { intent.putExtra(EXTRA_UUID_CHAR, characteristic.getUuid().toString());
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override // Always try to add the RAW value
public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) { final byte[] data = characteristic.getValue();
if (status == BluetoothGatt.GATT_SUCCESS) { if (data != null && data.length > 0) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); intent.putExtra(EXTRA_DATA_RAW, data);
} }
}
@Override sendBroadcast(intent);
public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { }
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
private void broadcastUpdate(final String action) { /**
final Intent intent = new Intent(action); * After using a given BLE device, the app must call this method to ensure resources are
sendBroadcast(intent); * released properly.
} */
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { /**
final Intent intent = new Intent(action); * Connects to the GATT server hosted on the Bluetooth LE device.
intent.putExtra(EXTRA_UUID_CHAR, characteristic.getUuid().toString()); *
* @param address The device address of the destination device.
* @return Return true if the connection is initiated successfully. The connection result
* is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Always try to add the RAW value // Previously connected device. Try to reconnect.
final byte[] data = characteristic.getValue(); if (mBluetoothDeviceAddress != null
if (data != null && data.length > 0) { && address.equals(mBluetoothDeviceAddress)
intent.putExtra(EXTRA_DATA_RAW, data); && mBluetoothGatt != null) {
}
sendBroadcast(intent); Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
} if (mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
public class LocalBinder extends Binder { final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
public BluetoothLeService getService() { if (device == null) {
return BluetoothLeService.this; Log.w(TAG, "Device not found. Unable to connect.");
} return false;
} }
// 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.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
@Override /**
public IBinder onBind(final Intent intent) { * Disconnects an existing connection or cancel a pending connection. The disconnection result
return mBinder; * is reported asynchronously through the
} * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect();
}
@Override /**
public boolean onUnbind(final Intent intent) { * Retrieves a list of supported GATT services on the connected device. This should be
// After using a given device, you should make sure that BluetoothGatt.close() is called * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
// such that resources are cleaned up properly. In this particular example, close() is *
// invoked when the UI is disconnected from the Service. * @return A {@code List} of supported services.
close(); */
return super.onUnbind(intent); public List<BluetoothGattService> getSupportedGattServices() {
} if (mBluetoothGatt == null) return null;
private final IBinder mBinder = new LocalBinder(); return mBluetoothGatt.getServices();
}
/** /**
* Initializes a reference to the local Bluetooth adapter. * Initializes a reference to the local Bluetooth adapter.
* *
* @return Return true if the initialization is successful. * @return Return true if the initialization is successful.
*/ */
public boolean initialize() { public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter through // For API level 18 and above, get a reference to BluetoothAdapter through
// BluetoothManager. // BluetoothManager.
if (mBluetoothManager == null) { if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) { if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager."); Log.e(TAG, "Unable to initialize BluetoothManager.");
return false; return false;
} }
} }
mBluetoothAdapter = mBluetoothManager.getAdapter(); mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) { if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter."); Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false; return false;
} }
return true; return true;
} }
/** @Override
* Connects to the GATT server hosted on the Bluetooth LE device. public IBinder onBind(final Intent intent) {
* return mBinder;
* @param address The device address of the destination device. }
*
* @return Return true if the connection is initiated successfully. The connection result
* is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect. @Override
if (mBluetoothDeviceAddress != null public boolean onUnbind(final Intent intent) {
&& address.equals(mBluetoothDeviceAddress) // After using a given device, you should make sure that BluetoothGatt.close() is called
&& mBluetoothGatt != null) { // such that resources are cleaned up properly. In this particular example, close() is
// invoked when the UI is disconnected from the Service.
close();
return super.onUnbind(intent);
}
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); /**
if (mBluetoothGatt.connect()) { * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
mConnectionState = STATE_CONNECTING; * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
return true; * callback.
} else { *
return false; * @param characteristic The characteristic to read from.
} */
} public void readCharacteristic(final BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); /**
if (device == null) { * Enables or disables notification on a give characteristic.
Log.w(TAG, "Device not found. Unable to connect."); *
return false; * @param characteristic Characteristic to act on.
} * @param enabled If true, enable notification. False otherwise.
// We want to directly connect to the device, so we are setting the autoConnect */
// parameter to false. public void setCharacteristicNotification(final BluetoothGattCharacteristic characteristic, final boolean enabled) {
mBluetoothGatt = device.connectGatt(this, false, mGattCallback); if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.d(TAG, "Trying to create a new connection."); Log.w(TAG, "BluetoothAdapter not initialized");
mBluetoothDeviceAddress = address; return;
mConnectionState = STATE_CONNECTING; }
return true; mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
} }
/** public class LocalBinder extends Binder {
* Disconnects an existing connection or cancel a pending connection. The disconnection result public BluetoothLeService getService() {
* is reported asynchronously through the return BluetoothLeService.this;
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} }
* callback. }
*/
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect();
}
/**
* After using a given BLE device, the app must call this method to ensure resources are
* released properly.
*/
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
/**
* Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
* asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
* callback.
*
* @param characteristic The characteristic to read from.
*/
public void readCharacteristic(final BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
/**
* Enables or disables notification on a give characteristic.
*
* @param characteristic Characteristic to act on.
* @param enabled If true, enable notification. False otherwise.
*/
public void setCharacteristicNotification(final BluetoothGattCharacteristic characteristic, final boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}
/**
* Retrieves a list of supported GATT services on the connected device. This should be
* invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
*
* @return A {@code List} of supported services.
*/
public List<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices();
}
} }
@@ -5,40 +5,42 @@ import android.os.Handler;
import android.util.Log; import android.util.Log;
public class BluetoothLeScanner { public class BluetoothLeScanner {
private final Handler mHandler; private final Handler mHandler;
private final BluetoothAdapter.LeScanCallback mLeScanCallback; private final BluetoothAdapter.LeScanCallback mLeScanCallback;
private final BluetoothUtils mBluetoothUtils; private final BluetoothUtils mBluetoothUtils;
private boolean mScanning; private boolean mScanning;
public BluetoothLeScanner(final BluetoothAdapter.LeScanCallback leScanCallback, final BluetoothUtils bluetoothUtils){
mHandler = new Handler();
mLeScanCallback = leScanCallback;
mBluetoothUtils = bluetoothUtils;
}
public boolean isScanning() {
return mScanning;
}
public void scanLeDevice(final int duration, final boolean enable) { public BluetoothLeScanner(final BluetoothAdapter.LeScanCallback leScanCallback, final BluetoothUtils bluetoothUtils) {
mHandler = new Handler();
mLeScanCallback = leScanCallback;
mBluetoothUtils = bluetoothUtils;
}
public boolean isScanning() {
return mScanning;
}
public void scanLeDevice(final int duration, final boolean enable) {
if (enable) { if (enable) {
if(mScanning){return;} if (mScanning) {
Log.d("TAG", "~ Starting Scan"); return;
}
Log.d("TAG", "~ Starting Scan");
// Stops scanning after a pre-defined scan period. // Stops scanning after a pre-defined scan period.
if(duration > 0){ if (duration > 0) {
mHandler.postDelayed(new Runnable() { mHandler.postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
Log.d("TAG", "~ Stopping Scan (timeout)"); Log.d("TAG", "~ Stopping Scan (timeout)");
mScanning = false; mScanning = false;
mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback); mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback);
} }
}, duration); }, duration);
} }
mScanning = true; mScanning = true;
mBluetoothUtils.getBluetoothAdapter().startLeScan(mLeScanCallback); mBluetoothUtils.getBluetoothAdapter().startLeScan(mLeScanCallback);
} else { } else {
Log.d("TAG", "~ Stopping Scan"); Log.d("TAG", "~ Stopping Scan");
mScanning = false; mScanning = false;
mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback); mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback);
} }
@@ -8,37 +8,36 @@ import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
public final class BluetoothUtils { public final class BluetoothUtils {
private final Activity mActivity; public final static int REQUEST_ENABLE_BT = 2001;
private final BluetoothAdapter mBluetoothAdapter; private final Activity mActivity;
private final BluetoothAdapter mBluetoothAdapter;
public final static int REQUEST_ENABLE_BT = 2001; public BluetoothUtils(final Activity activity) {
mActivity = activity;
public BluetoothUtils(final Activity activity){ final BluetoothManager btManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE);
mActivity = activity; mBluetoothAdapter = btManager.getAdapter();
final BluetoothManager btManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE); }
mBluetoothAdapter = btManager.getAdapter();
} public void askUserToEnableBluetoothIfNeeded() {
if (isBluetoothLeSupported() && (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())) {
public void askUserToEnableBluetoothIfNeeded(){ final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
if (isBluetoothLeSupported() && (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())) { mActivity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); }
mActivity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }
}
} public BluetoothAdapter getBluetoothAdapter() {
return mBluetoothAdapter;
public BluetoothAdapter getBluetoothAdapter(){ }
return mBluetoothAdapter;
} public boolean isBluetoothLeSupported() {
return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
public boolean isBluetoothLeSupported(){ }
return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
} public boolean isBluetoothOn() {
if (mBluetoothAdapter == null) {
public boolean isBluetoothOn(){ return false;
if (mBluetoothAdapter == null) { } else {
return false; return mBluetoothAdapter.isEnabled();
} else { }
return mBluetoothAdapter.isEnabled(); }
}
}
} }
@@ -3,6 +3,6 @@ package uk.co.alt236.btlescan.util;
import java.text.DecimalFormat; import java.text.DecimalFormat;
public class Constants { public class Constants {
public static final DecimalFormat DOUBLE_TWO_DIGIT_ACCURACY = new DecimalFormat("#.##"); public static final DecimalFormat DOUBLE_TWO_DIGIT_ACCURACY = new DecimalFormat("#.##");
public static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; public static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
} }
@@ -1,23 +1,26 @@
package uk.co.alt236.btlescan.util; package uk.co.alt236.btlescan.util;
public class CsvWriterHelper { public class CsvWriterHelper {
private static final String QUOTE = "\""; private static final String QUOTE = "\"";
public static String addStuff(final Integer text){
return QUOTE + text + QUOTE + ",";
}
public static String addStuff(final Long text){ public static String addStuff(final Integer text) {
return QUOTE + text + QUOTE + ","; return QUOTE + text + QUOTE + ",";
} }
public static String addStuff(final boolean value){ public static String addStuff(final Long text) {
return QUOTE + value + QUOTE + ","; return QUOTE + text + QUOTE + ",";
} }
public static String addStuff(String text){
if(text == null){text = "<blank>";}
text = text.replace(QUOTE, "'");
return QUOTE + text.trim() + QUOTE + ","; public static String addStuff(final boolean value) {
} return QUOTE + value + QUOTE + ",";
}
public static String addStuff(String text) {
if (text == null) {
text = "<blank>";
}
text = text.replace(QUOTE, "'");
return QUOTE + text.trim() + QUOTE + ",";
}
} }
@@ -5,14 +5,14 @@ import java.util.Date;
import java.util.Locale; import java.util.Locale;
public class TimeFormatter { public class TimeFormatter {
private final static String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz"; private final static String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz";
private final static SimpleDateFormat ISO_FORMATTER = new UtcDateFormatter(ISO_FORMAT, Locale.US); private final static SimpleDateFormat ISO_FORMATTER = new UtcDateFormatter(ISO_FORMAT, Locale.US);
public static String getIsoDateTime(final Date date){ public static String getIsoDateTime(final Date date) {
return ISO_FORMATTER.format(date); return ISO_FORMATTER.format(date);
} }
public static String getIsoDateTime(final long millis){ public static String getIsoDateTime(final long millis) {
return getIsoDateTime(new Date(millis)); return getIsoDateTime(new Date(millis));
} }
} }
@@ -1,72 +1,74 @@
/******************************************************************************* /**
* ****************************************************************************
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
* * <p/>
* GenieConnect Ltd. ("COMPANY") CONFIDENTIAL * GenieConnect Ltd. ("COMPANY") CONFIDENTIAL
* Unpublished Copyright (c) 2010-2013 GenieConnect Ltd., All Rights Reserved. * Unpublished Copyright (c) 2010-2013 GenieConnect Ltd., All Rights Reserved.
* * <p/>
* NOTICE: * NOTICE:
* All information contained herein is, and remains the property of COMPANY. * All information contained herein is, and remains the property of COMPANY.
* The intellectual and technical concepts contained herein are proprietary to * The intellectual and technical concepts contained herein are proprietary to
* COMPANY and may be covered by U.S. and Foreign Patents, patents in process, and * COMPANY and may be covered by U.S. and Foreign Patents, patents in process, and
* are protected by trade secret or copyright law. Dissemination of this * are protected by trade secret or copyright law. Dissemination of this
* information or reproduction of this material is strictly forbidden unless prior * information or reproduction of this material is strictly forbidden unless prior
* written permission is obtained from COMPANY. Access to the source code * written permission is obtained from COMPANY. Access to the source code
* contained herein is hereby forbidden to anyone except current COMPANY employees, * contained herein is hereby forbidden to anyone except current COMPANY employees,
* managers or contractors who have executed Confidentiality and Non-disclosure * managers or contractors who have executed Confidentiality and Non-disclosure
* agreements explicitly covering such access. * agreements explicitly covering such access.
* * <p/>
* The copyright notice above does not evidence any actual or intended publication * The copyright notice above does not evidence any actual or intended publication
* or disclosure of this source code, which includes information that is * or disclosure of this source code, which includes information that is
* confidential and/or proprietary, and is a trade secret, of COMPANY. * confidential and/or proprietary, and is a trade secret, of COMPANY.
* * <p/>
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, OR PUBLIC * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, OR PUBLIC
* DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT THE EXPRESS WRITTEN * DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT THE EXPRESS WRITTEN
* CONSENT OF COMPANY IS STRICTLY PROHIBITED, AND IN VIOLATION OF APPLICABLE LAWS * CONSENT OF COMPANY IS STRICTLY PROHIBITED, AND IN VIOLATION OF APPLICABLE LAWS
* AND INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE CODE * AND INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE CODE
* AND/OR RELATED INFORMATION DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, * AND/OR RELATED INFORMATION DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE,
* DISCLOSE OR DISTRIBUTE ITS CONTENTS, OR TO MANUFACTURE, USE, OR SELL ANYTHING * DISCLOSE OR DISTRIBUTE ITS CONTENTS, OR TO MANUFACTURE, USE, OR SELL ANYTHING
* THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. * THAT IT MAY DESCRIBE, IN WHOLE OR IN PART.
******************************************************************************/ * ****************************************************************************
*/
package uk.co.alt236.btlescan.util; package uk.co.alt236.btlescan.util;
import android.annotation.SuppressLint;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import android.annotation.SuppressLint; public class UtcDateFormatter extends java.text.SimpleDateFormat {
private static final long serialVersionUID = 1L;
public class UtcDateFormatter extends java.text.SimpleDateFormat{ private static final String TIME_ZONE_STRING = "UTC";
private static final long serialVersionUID = 1L; private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone(TIME_ZONE_STRING);
private static final String TIME_ZONE_STRING = "UTC"; @SuppressLint("SimpleDateFormat")
private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone(TIME_ZONE_STRING); public UtcDateFormatter(final String template) {
super(template);
@SuppressLint("SimpleDateFormat") super.setTimeZone(TIME_ZONE_UTC);
public UtcDateFormatter(final String template){ }
super(template);
super.setTimeZone(TIME_ZONE_UTC); @SuppressLint("SimpleDateFormat")
} public UtcDateFormatter(final String template, final DateFormatSymbols symbols) {
super(template, symbols);
@SuppressLint("SimpleDateFormat") super.setTimeZone(TIME_ZONE_UTC);
public UtcDateFormatter(final String template, final DateFormatSymbols symbols){ }
super(template, symbols);
super.setTimeZone(TIME_ZONE_UTC); public UtcDateFormatter(final String template, final Locale locale) {
} super(template, locale);
super.setTimeZone(TIME_ZONE_UTC);
public UtcDateFormatter(final String template, final Locale locale){ }
super(template, locale);
super.setTimeZone(TIME_ZONE_UTC); /*
} * This function will throw an UnsupportedOperationException.
* You are not be able to change the TimeZone of this object
/* *
* This function will throw an UnsupportedOperationException. * (non-Javadoc)
* You are not be able to change the TimeZone of this object * @see java.text.DateFormat#setTimeZone(java.util.TimeZone)
* */
* (non-Javadoc) @Override
* @see java.text.DateFormat#setTimeZone(java.util.TimeZone) public void setTimeZone(final TimeZone timezone) {
*/ throw new UnsupportedOperationException("This SimpleDateFormat can only be in " + TIME_ZONE_STRING);
@Override }
public void setTimeZone(final TimeZone timezone){
throw new UnsupportedOperationException("This SimpleDateFormat can only be in " + TIME_ZONE_STRING);
}
} }
@@ -1,11 +1,11 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="56dp" android:layout_width="56dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minWidth="56dp" > android:minWidth="56dp">
<ProgressBar <ProgressBar
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="32dp" android:layout_height="32dp"
android:layout_gravity="center" /> android:layout_gravity="center"/>
</FrameLayout> </FrameLayout>
@@ -1,15 +1,14 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin">
android:paddingTop="@dimen/activity_vertical_margin" >
<ListView <ListView
android:id="@android:id/list" android:id="@android:id/list"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" > android:layout_height="wrap_content">
</ListView> </ListView>
</RelativeLayout> </RelativeLayout>
@@ -15,13 +15,13 @@
limitations under the License. limitations under the License.
--> -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" > android:paddingTop="@dimen/activity_vertical_margin">
<GridLayout <GridLayout
android:id="@+id/deviceInformation" android:id="@+id/deviceInformation"
@@ -29,27 +29,27 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:columnCount="2" android:columnCount="2"
android:useDefaultMargins="true" > android:useDefaultMargins="true">
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_device_address" /> android:text="@string/label_device_address"/>
<TextView <TextView
android:id="@+id/device_address" android:id="@+id/device_address"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_state" /> android:text="@string/label_state"/>
<TextView <TextView
android:id="@+id/connection_state" android:id="@+id/connection_state"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
</GridLayout> </GridLayout>
<View <View
@@ -57,7 +57,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_below="@id/deviceInformation" android:layout_below="@id/deviceInformation"
android:background="@android:color/holo_blue_dark" /> android:background="@android:color/holo_blue_dark"/>
<GridLayout <GridLayout
android:id="@+id/gattInformation" android:id="@+id/gattInformation"
@@ -65,47 +65,47 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:columnCount="2" android:columnCount="2"
android:useDefaultMargins="true" > android:useDefaultMargins="true">
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_uuid" /> android:text="@string/label_uuid"/>
<TextView <TextView
android:id="@+id/uuid" android:id="@+id/uuid"
style="@style/GridLayoutDataTextViewMonospace" /> style="@style/GridLayoutDataTextViewMonospace"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_desc" /> android:text="@string/label_desc"/>
<TextView <TextView
android:id="@+id/description" android:id="@+id/description"
style="@style/GridLayoutDataTextViewMonospace" /> style="@style/GridLayoutDataTextViewMonospace"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_as_string" /> android:text="@string/label_as_string"/>
<TextView <TextView
android:id="@+id/data_as_string" android:id="@+id/data_as_string"
style="@style/GridLayoutDataTextViewMonospace" /> style="@style/GridLayoutDataTextViewMonospace"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_as_array" /> android:text="@string/label_as_array"/>
<TextView <TextView
android:id="@+id/data_as_array" android:id="@+id/data_as_array"
style="@style/GridLayoutDataTextViewMonospace" /> style="@style/GridLayoutDataTextViewMonospace"/>
</GridLayout> </GridLayout>
<View <View
@@ -113,13 +113,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_above="@id/gattInformation" android:layout_above="@id/gattInformation"
android:background="@android:color/holo_blue_dark" /> android:background="@android:color/holo_blue_dark"/>
<ExpandableListView <ExpandableListView
android:id="@+id/gatt_services_list" android:id="@+id/gatt_services_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_above="@id/lowerSepparator" android:layout_above="@id/lowerSepparator"
android:layout_below="@id/upperSepparator" /> android:layout_below="@id/upperSepparator"/>
</RelativeLayout> </RelativeLayout>
@@ -1,20 +1,20 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" > tools:context=".MainActivity">
<GridLayout <GridLayout
android:id="@+id/gridLayout1" android:id="@+id/gridLayout1"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:columnCount="2" android:columnCount="2"
android:paddingBottom="@dimen/activity_vertical_margin" > android:paddingBottom="@dimen/activity_vertical_margin">
<TextView <TextView
android:layout_column="0" android:layout_column="0"
@@ -22,7 +22,7 @@
android:layout_row="0" android:layout_row="0"
android:text="@string/label_bluetooth_le_status" android:text="@string/label_bluetooth_le_status"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/tvBluetoothLe" android:id="@+id/tvBluetoothLe"
@@ -33,7 +33,7 @@
android:layout_row="0" android:layout_row="0"
android:gravity="right" android:gravity="right"
android:text="@string/not_supported" android:text="@string/not_supported"
android:textSize="12sp" /> android:textSize="12sp"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -43,7 +43,7 @@
android:layout_row="1" android:layout_row="1"
android:text="@string/label_bluetooth_status" android:text="@string/label_bluetooth_status"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/tvBluetoothStatus" android:id="@+id/tvBluetoothStatus"
@@ -54,59 +54,59 @@
android:layout_row="1" android:layout_row="1"
android:gravity="right" android:gravity="right"
android:text="@string/off" android:text="@string/off"
android:textSize="12sp" /> android:textSize="12sp"/>
</GridLayout> </GridLayout>
<View <View
android:id="@+id/upperSepparator" android:id="@+id/upperSepparator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:background="@android:color/holo_blue_dark" /> android:background="@android:color/holo_blue_dark"/>
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" > android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/infoContainer" android:id="@+id/infoContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:orientation="vertical" > android:orientation="vertical">
<View <View
android:id="@+id/lowerSepparator" android:id="@+id/lowerSepparator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:background="@android:color/holo_blue_dark" /> android:background="@android:color/holo_blue_dark"/>
<TextView <TextView
android:id="@+id/tvItemCount" android:id="@+id/tvItemCount"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/formatter_item_count" /> android:text="@string/formatter_item_count"/>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/listContainer" android:id="@+id/listContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_above="@id/infoContainer" android:layout_above="@id/infoContainer"
android:orientation="vertical" > android:layout_alignParentTop="true"
android:orientation="vertical">
<ListView <ListView
android:id="@android:id/list" android:id="@android:id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"/>
<TextView <TextView
android:id="@android:id/empty" android:id="@android:id/empty"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal" android:gravity="center_vertical|center_horizontal"
android:text="@string/no_data" /> android:text="@string/no_data"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
@@ -15,34 +15,34 @@
limitations under the License. limitations under the License.
--> -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="top" android:gravity="top"
android:orientation="horizontal" > android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/device_icon" android:id="@+id/device_icon"
android:paddingTop="5dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingRight="5dp" android:paddingRight="5dp"
android:src="@drawable/ic_bluetooth" /> android:paddingTop="5dp"
android:src="@drawable/ic_bluetooth"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" > android:orientation="vertical">
<TextView <TextView
android:id="@+id/device_name" android:id="@+id/device_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="24sp" /> android:textSize="24sp"/>
<GridLayout <GridLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:columnCount="2" > android:columnCount="2">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -50,14 +50,14 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="@string/label_mac" android:text="@string/label_mac"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/device_address" android:id="@+id/device_address"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="12sp" android:textSize="12sp"
android:typeface="monospace" /> android:typeface="monospace"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -65,14 +65,14 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="Updated:" android:text="Updated:"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/device_last_update" android:id="@+id/device_last_update"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -80,14 +80,14 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="@string/label_rssi" android:text="@string/label_rssi"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/device_rssi" android:id="@+id/device_rssi"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp"/>
</GridLayout> </GridLayout>
<GridLayout <GridLayout
@@ -95,7 +95,7 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/light_gray" android:background="@color/light_gray"
android:columnCount="4" > android:columnCount="4">
<!-- ROW 1 --> <!-- ROW 1 -->
@@ -105,7 +105,7 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="@string/label_uuid" android:text="@string/label_uuid"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/ibeacon_uuid" android:id="@+id/ibeacon_uuid"
@@ -113,7 +113,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_columnSpan="3" android:layout_columnSpan="3"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp"/>
<!-- ROW 2 --> <!-- ROW 2 -->
@@ -123,14 +123,14 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="@string/label_major" android:text="@string/label_major"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/ibeacon_major" android:id="@+id/ibeacon_major"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -139,14 +139,14 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="@string/label_minor" android:text="@string/label_minor"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/ibeacon_minor" android:id="@+id/ibeacon_minor"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp"/>
<!-- ROW 3 --> <!-- ROW 3 -->
@@ -156,14 +156,14 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="@string/label_tx_power" android:text="@string/label_tx_power"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/ibeacon_tx_power" android:id="@+id/ibeacon_tx_power"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -172,14 +172,14 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="@string/label_distance" android:text="@string/label_distance"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/ibeacon_distance" android:id="@+id/ibeacon_distance"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp"/>
<!-- ROW 4 --> <!-- ROW 4 -->
@@ -189,7 +189,7 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="Descriptor:" android:text="Descriptor:"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<TextView <TextView
android:id="@+id/ibeacon_distance_descriptor" android:id="@+id/ibeacon_distance_descriptor"
@@ -197,7 +197,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_columnSpan="3" android:layout_columnSpan="3"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp"/>
</GridLayout> </GridLayout>
</LinearLayout> </LinearLayout>
@@ -1,43 +1,43 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="top" android:gravity="top"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dp" > android:paddingBottom="5dp">
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<GridLayout <GridLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:columnCount="2" android:columnCount="2"
android:useDefaultMargins="true" > android:useDefaultMargins="true">
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_as_string" /> android:text="@string/label_as_string"/>
<TextView <TextView
android:id="@+id/data_as_string" android:id="@+id/data_as_string"
style="@style/GridLayoutDataTextViewMonospace" /> style="@style/GridLayoutDataTextViewMonospace"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_as_array" /> android:text="@string/label_as_array"/>
<TextView <TextView
android:id="@+id/data_as_array" android:id="@+id/data_as_array"
style="@style/GridLayoutDataTextViewMonospace" /> style="@style/GridLayoutDataTextViewMonospace"/>
</GridLayout> </GridLayout>
</LinearLayout> </LinearLayout>
@@ -1,56 +1,56 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="top" android:gravity="top"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dp" > android:paddingBottom="5dp">
<GridLayout <GridLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:columnCount="2" android:columnCount="2"
android:useDefaultMargins="true" > android:useDefaultMargins="true">
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_device_name" /> android:text="@string/label_device_name"/>
<TextView <TextView
android:id="@+id/deviceName" android:id="@+id/deviceName"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_device_address" /> android:text="@string/label_device_address"/>
<TextView <TextView
android:id="@+id/deviceAddress" android:id="@+id/deviceAddress"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_device_class" /> android:text="@string/label_device_class"/>
<TextView <TextView
android:id="@+id/deviceClass" android:id="@+id/deviceClass"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_bonding_state" /> android:text="@string/label_bonding_state"/>
<TextView <TextView
android:id="@+id/deviceBondingState" android:id="@+id/deviceBondingState"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
</GridLayout> </GridLayout>
</LinearLayout> </LinearLayout>
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="top" android:gravity="top"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dp" > android:paddingBottom="5dp">
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
@@ -13,11 +13,11 @@
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="@android:color/holo_blue_dark" android:textColor="@android:color/holo_blue_dark"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold" /> android:textStyle="bold"/>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:background="@android:color/holo_blue_dark" /> android:background="@android:color/holo_blue_dark"/>
</LinearLayout> </LinearLayout>
@@ -1,76 +1,76 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="top" android:gravity="top"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dp" > android:paddingBottom="5dp">
<GridLayout <GridLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:columnCount="2" android:columnCount="2"
android:useDefaultMargins="true" > android:useDefaultMargins="true">
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_company_id" /> android:text="@string/label_company_id"/>
<TextView <TextView
android:id="@+id/companyId" android:id="@+id/companyId"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_advertisement" /> android:text="@string/label_advertisement"/>
<TextView <TextView
android:id="@+id/advertisement" android:id="@+id/advertisement"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_uuid" /> android:text="@string/label_uuid"/>
<TextView <TextView
android:id="@+id/uuid" android:id="@+id/uuid"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_major" /> android:text="@string/label_major"/>
<TextView <TextView
android:id="@+id/major" android:id="@+id/major"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_minor" /> android:text="@string/label_minor"/>
<TextView <TextView
android:id="@+id/minor" android:id="@+id/minor"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_tx_power" /> android:text="@string/label_tx_power"/>
<TextView <TextView
android:id="@+id/txpower" android:id="@+id/txpower"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
</GridLayout> </GridLayout>
</LinearLayout> </LinearLayout>
@@ -1,66 +1,66 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="top" android:gravity="top"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dp" > android:paddingBottom="5dp">
<GridLayout <GridLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:columnCount="2" android:columnCount="2"
android:useDefaultMargins="true" > android:useDefaultMargins="true">
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_first_timestamp" /> android:text="@string/label_first_timestamp"/>
<TextView <TextView
android:id="@+id/firstTimestamp" android:id="@+id/firstTimestamp"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_first_rssi" /> android:text="@string/label_first_rssi"/>
<TextView <TextView
android:id="@+id/firstRssi" android:id="@+id/firstRssi"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_last_timestamp" /> android:text="@string/label_last_timestamp"/>
<TextView <TextView
android:id="@+id/lastTimestamp" android:id="@+id/lastTimestamp"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_last_rssi" /> android:text="@string/label_last_rssi"/>
<TextView <TextView
android:id="@+id/lastRssi" android:id="@+id/lastRssi"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
<TextView <TextView
style="@style/GridLayoutTitleTextView" style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/label_running_average_rssi" /> android:text="@string/label_running_average_rssi"/>
<TextView <TextView
android:id="@+id/runningAverageRssi" android:id="@+id/runningAverageRssi"
style="@style/GridLayoutDataTextView" /> style="@style/GridLayoutDataTextView"/>
</GridLayout> </GridLayout>
</LinearLayout> </LinearLayout>
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="top" android:gravity="top"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dp" > android:paddingBottom="5dp">
<TextView <TextView
android:id="@+id/data" android:id="@+id/data"
style="@style/GridLayoutDataTextViewMonospace" /> style="@style/GridLayoutDataTextViewMonospace"/>
</LinearLayout> </LinearLayout>
+1 -1
View File
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:id="@+id/menu_refresh" android:id="@+id/menu_refresh"
+2 -2
View File
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:id="@+id/menu_refresh" android:id="@+id/menu_refresh"
@@ -33,9 +33,9 @@
android:title="@string/menu_stop"/> android:title="@string/menu_stop"/>
<item <item
android:id="@+id/menu_share" android:id="@+id/menu_share"
android:icon="@drawable/ic_action_share"
android:orderInCategory="102" android:orderInCategory="102"
android:showAsAction="ifRoom" android:showAsAction="ifRoom"
android:icon="@drawable/ic_action_share"
android:title="@string/menu_share"/> android:title="@string/menu_share"/>
<item <item
android:id="@+id/menu_about" android:id="@+id/menu_about"
+1 -1
View File
@@ -1,3 +1,3 @@
<resources> <resources>
<color name="light_gray">#66e0e0e0</color> <color name="light_gray">#66e0e0e0</color>
</resources> </resources>