diff --git a/sample_app/libs/merge-1.0.1.jar b/sample_app/libs/merge-1.0.1.jar new file mode 100644 index 0000000..fb49c15 Binary files /dev/null and b/sample_app/libs/merge-1.0.1.jar differ diff --git a/sample_app/libs/sacklist-1.0.0.jar b/sample_app/libs/sacklist-1.0.0.jar new file mode 100644 index 0000000..3a49a76 Binary files /dev/null and b/sample_app/libs/sacklist-1.0.0.jar differ diff --git a/sample_app/res/layout/activity_details.xml b/sample_app/res/layout/activity_details.xml index 011edb7..e853847 100644 --- a/sample_app/res/layout/activity_details.xml +++ b/sample_app/res/layout/activity_details.xml @@ -7,16 +7,9 @@ android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > - - - - - + \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_adrecord.xml b/sample_app/res/layout/list_item_view_adrecord.xml new file mode 100644 index 0000000..ed0adc6 --- /dev/null +++ b/sample_app/res/layout/list_item_view_adrecord.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_device_info.xml b/sample_app/res/layout/list_item_view_device_info.xml new file mode 100644 index 0000000..04ddfe7 --- /dev/null +++ b/sample_app/res/layout/list_item_view_device_info.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_header.xml b/sample_app/res/layout/list_item_view_header.xml new file mode 100644 index 0000000..b218ade --- /dev/null +++ b/sample_app/res/layout/list_item_view_header.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_ibeacon_details.xml b/sample_app/res/layout/list_item_view_ibeacon_details.xml new file mode 100644 index 0000000..cd07cc9 --- /dev/null +++ b/sample_app/res/layout/list_item_view_ibeacon_details.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_rssi_info.xml b/sample_app/res/layout/list_item_view_rssi_info.xml new file mode 100644 index 0000000..5b515cf --- /dev/null +++ b/sample_app/res/layout/list_item_view_rssi_info.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_textview.xml b/sample_app/res/layout/list_item_view_textview.xml new file mode 100644 index 0000000..1f00684 --- /dev/null +++ b/sample_app/res/layout/list_item_view_textview.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/sample_app/res/values/strings.xml b/sample_app/res/values/strings.xml index 4c6cd43..5d57739 100644 --- a/sample_app/res/values/strings.xml +++ b/sample_app/res/values/strings.xml @@ -44,5 +44,12 @@ Bluetooth LE Scan Results (%s) Please find attached the scan results. Please select your email client: + iBeacon Data + Raw Ad Records + Scan Record + RSSI Info + Device Info + Invalid Device Data! + unknown \ No newline at end of file diff --git a/sample_app/res/values/styles.xml b/sample_app/res/values/styles.xml index 6ce89c7..9cf5fd9 100644 --- a/sample_app/res/values/styles.xml +++ b/sample_app/res/values/styles.xml @@ -1,4 +1,4 @@ - + - + + + + + + + \ No newline at end of file diff --git a/sample_app/src/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java b/sample_app/src/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java index 2581279..75e91b7 100644 --- a/sample_app/src/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java +++ b/sample_app/src/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java @@ -1,32 +1,121 @@ package uk.co.alt236.btlescan.activities; import java.util.Collection; +import java.util.Locale; import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; import uk.co.alt236.bluetoothlelib.device.mfdata.IBeaconManufacturerData; +import uk.co.alt236.bluetoothlelib.resolvers.CompanyIdentifierResolver; import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; import uk.co.alt236.bluetoothlelib.util.ByteUtils; import uk.co.alt236.bluetoothlelib.util.IBeaconUtils; import uk.co.alt236.btlescan.R; -import uk.co.alt236.btlescan.util.Constants; -import android.app.Activity; +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 butterknife.InjectView; -public class DeviceDetailsActivity extends Activity{ - private static final String SECTION_LINE = "------------------------------"; +import com.commonsware.cwac.merge.MergeAdapter; + +public class DeviceDetailsActivity extends ListActivity{ public static final String EXTRA_DEVICE = "extra_device"; - @InjectView(R.id.tvDetails) TextView mTvDetails; - private BluetoothLeDevice mDevice; + private void appendAdRecordView(MergeAdapter adapter, String title, 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); + + tvTitle.setText(title ); + tvString.setText("'" + AdRecordUtils.getRecordDataAsString(record) + "'"); + tvArray.setText("'" + ByteUtils.byteArrayToHexString(record.getData()) + "'"); + + adapter.addView(lt); + } + + private void appendDeviceInfo(MergeAdapter adapter, 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); + + tvName.setText(device.getName()); + tvAddress.setText(device.getAddress()); + tvClass.setText(device.getBluetoothDeviceClassName()); + tvBondingState.setText(device.getBluetoothDeviceBondState()); + + adapter.addView(lt); + } + + private void appendHeader(MergeAdapter adapter, 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); + + adapter.addView(lt); + } + + private void appendIBeaconInfo(MergeAdapter adapter, 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); + + tvCompanyId.setText( + CompanyIdentifierResolver.getCompanyName(iBeaconData.getCompanyIdentifier(), getString(R.string.unknown)) + + " (" + hexEncode(iBeaconData.getCompanyIdentifier()) + ")"); + tvAdvert.setText(iBeaconData.getIBeaconAdvertisement() + " (" + hexEncode( iBeaconData.getIBeaconAdvertisement() ) + ")"); + tvUUID.setText(iBeaconData.getUUID().toString()); + 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(MergeAdapter adapter, 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); + + tvFirstTimestamp.setText(formatTime(device.getFirstTimestamp())); + 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(MergeAdapter adapter, byte data[]){ + appendSimpleText(adapter, ByteUtils.byteArrayToHexString(data)); + } + + private void appendSimpleText(MergeAdapter adapter, String 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); + } + + private String formatRssi(double rssi){ return getString(R.string.formatter_db, String.valueOf(rssi)); } @@ -35,11 +124,6 @@ public class DeviceDetailsActivity extends Activity{ return getString(R.string.formatter_db, String.valueOf(rssi)); } - private String formatTime(long time){ - return android.text.format.DateFormat.format( - Constants.TIME_FORMAT, new java.util.Date(time)).toString(); - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -62,11 +146,11 @@ public class DeviceDetailsActivity extends Activity{ switch (item.getItemId()) { case R.id.menu_connect: - final Intent intent = new Intent(this, DeviceControlActivity.class); - intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, mDevice.getName()); - intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, mDevice.getAddress()); + final Intent intent = new Intent(this, DeviceControlActivity.class); + intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, mDevice.getName()); + intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, mDevice.getAddress()); - startActivity(intent); + startActivity(intent); break; } @@ -74,85 +158,50 @@ public class DeviceDetailsActivity extends Activity{ } private void pupulateDetails(BluetoothLeDevice device) { - final StringBuilder sb = new StringBuilder(); + final MergeAdapter adapter = new MergeAdapter(); if(device == null){ - append(sb, "Invalid Device Data!", null); + appendHeader(adapter, getString(R.string.header_device_info)); + appendSimpleText(adapter, getString(R.string.invalid_device_data)); } else { - append(sb, "Device Info", null); - append(sb, SECTION_LINE, null); - append(sb, "Device Name", device.getName()); - append(sb, "Device Address", device.getAddress()); + appendHeader(adapter, getString(R.string.header_device_info)); + appendDeviceInfo(adapter, device); - append(sb, "", null); - append(sb, "Device Class", device.getBluetoothDeviceClassName()); - append(sb, "Bonding State", device.getBluetoothDeviceBondState()); - - - append(sb, "", null); - append(sb, "RSSI Info", null); - append(sb, SECTION_LINE, null); - append(sb, "First Timestamp", formatTime(device.getFirstTimestamp())); - append(sb, "First RSSI", formatRssi(device.getFirstRssi())); - append(sb, "Current Timestamp", formatTime(device.getTimestamp())); - append(sb, "Current RSSI", formatRssi(device.getRssi())); - append(sb, "Running Average RSSI", formatRssi(device.getRunningAverageRssi())); - - append(sb, "", null); - append(sb, "Scan Record", null); - append(sb, SECTION_LINE, null); - append(sb, device.getScanRecord()); - - append(sb, "", null); - append(sb, "Raw Ad Records As String", null); - append(sb, SECTION_LINE, null); + appendHeader(adapter, getString(R.string.header_rssi_info)); + appendRssiInfo(adapter, device); + appendHeader(adapter, getString(R.string.header_scan_record)); + appendSimpleText(adapter, device.getScanRecord()); final Collection adRecords = device.getAdRecordStore().getRecordsAsCollection(); + if(adRecords.size() > 0){ + appendHeader(adapter, getString(R.string.header_raw_ad_records)); - for(final AdRecord record : adRecords){ - append(sb, "#" +record.getType() + " " + record.getHumanReadableType(), null); - append(sb, " As String: '" + AdRecordUtils.getRecordDataAsString(record) + "'", null); - append(sb, " As Array : " + ByteUtils.byteArrayToHexString(record.getData()), null); - append(sb, "", null); + for(final AdRecord record : adRecords){ + + appendAdRecordView( + adapter, + "#" + record.getType() + " " + record.getHumanReadableType(), + record); + } } - append(sb, "Additional", null); - append(sb, SECTION_LINE, null); - final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device); - append(sb, "Is iBeacon", isIBeacon); - + final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device); if(isIBeacon){ final IBeaconManufacturerData iBeaconData = new IBeaconManufacturerData(device); - append(sb, "Company ID", iBeaconData.getCompanyIdentifier() + " (" + Integer.toHexString( iBeaconData.getCompanyIdentifier() ) + ")"); - append(sb, "Advertisment", iBeaconData.getIBeaconAdvertisement() + " (" + Integer.toHexString( iBeaconData.getIBeaconAdvertisement() ) + ")"); - append(sb, "UUID", iBeaconData.getUUID().toString()); - append(sb, "Major", iBeaconData.getMajor() + " (" + Integer.toHexString( iBeaconData.getMajor() ) + ")"); - append(sb, "Minor", iBeaconData.getMinor() + " (" + Integer.toHexString( iBeaconData.getMinor() ) + ")"); - append(sb, "TX Power", iBeaconData.getCalibratedTxPower() + " (" + Integer.toHexString( iBeaconData.getCalibratedTxPower() ) + ")"); + appendHeader(adapter, getString(R.string.header_ibeacon_data)); + appendIBeaconInfo(adapter, iBeaconData); } + } - - mTvDetails.setText(sb.toString()); + getListView().setAdapter(adapter); } - private static void append(StringBuilder sb, byte[] value){ - append(sb, ByteUtils.byteArrayToHexString(value), null); + private static String formatTime(long time){ + return TimeFormatter.getIsoDateTime(time); } - private static void append(StringBuilder sb, String label, boolean value) { - append(sb, label, String.valueOf(value)); - } - - private static void append(StringBuilder sb, String label, String value){ - if(value != null){ - sb.append("\u2022" + padRight(label, 10) +":\t" + value + "\n"); - } else { - sb.append(label + "\n"); - } - } - - public static String padRight(String s, int n) { - return String.format("%1$-" + n + "s", s); + private static String hexEncode(int integer){ + return "0x" + Integer.toHexString(integer).toUpperCase(Locale.US); } }