Making the Device Details activity pretty

This commit is contained in:
Alexandros Schillings
2014-04-25 12:55:25 +01:00
parent e28d698ba9
commit cf48721d77
12 changed files with 436 additions and 90 deletions
Binary file not shown.
Binary file not shown.
+3 -10
View File
@@ -7,16 +7,9 @@
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
<ScrollView
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tvDetails"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:typeface="monospace" />
</ScrollView>
</ListView>
</RelativeLayout>
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
android:paddingBottom="5dp" >
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textStyle="bold" />
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:useDefaultMargins="true" >
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="As String:" />
<TextView
android:id="@+id/data_as_string"
style="@style/GridLayoutDataTextViewMonospace" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="As Array:" />
<TextView
android:id="@+id/data_as_array"
style="@style/GridLayoutDataTextViewMonospace" />
</GridLayout>
</LinearLayout>
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
android:paddingBottom="5dp" >
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:useDefaultMargins="true" >
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Device Name:" />
<TextView
android:id="@+id/deviceName"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Device Address" />
<TextView
android:id="@+id/deviceAddress"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Device Class:" />
<TextView
android:id="@+id/deviceClass"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bonding State" />
<TextView
android:id="@+id/deviceBondingState"
style="@style/GridLayoutDataTextView" />
</GridLayout>
</LinearLayout>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
android:paddingBottom="5dp" >
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="true"
android:textColor="@android:color/holo_blue_dark"
android:textSize="14sp"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/holo_blue_dark" />
</LinearLayout>
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
android:paddingBottom="5dp" >
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:useDefaultMargins="true" >
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Company ID:" />
<TextView
android:id="@+id/companyId"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Advertisement:" />
<TextView
android:id="@+id/advertisement"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="UUID:" />
<TextView
android:id="@+id/uuid"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Major:" />
<TextView
android:id="@+id/major"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Minor:" />
<TextView
android:id="@+id/minor"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TX Power:" />
<TextView
android:id="@+id/txpower"
style="@style/GridLayoutDataTextView" />
</GridLayout>
</LinearLayout>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
android:paddingBottom="5dp" >
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:useDefaultMargins="true" >
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="First Timestamp:" />
<TextView
android:id="@+id/firstTimestamp"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="First RSSI:" />
<TextView
android:id="@+id/firstRssi"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Last Timestamp:" />
<TextView
android:id="@+id/lastTimestamp"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Last RSSI:" />
<TextView
android:id="@+id/lastRssi"
style="@style/GridLayoutDataTextView" />
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Running Average RSSI:" />
<TextView
android:id="@+id/runningAverageRssi"
style="@style/GridLayoutDataTextView" />
</GridLayout>
</LinearLayout>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
android:paddingBottom="5dp" >
<TextView
android:id="@+id/data"
style="@style/GridLayoutDataTextViewMonospace" />
</LinearLayout>
+7
View File
@@ -44,5 +44,12 @@
<string name="exporter_email_subject">Bluetooth LE Scan Results (%s)</string>
<string name="exporter_email_body">Please find attached the scan results.</string>
<string name="exporter_email_picker_text">Please select your email client:</string>
<string name="header_ibeacon_data">iBeacon Data</string>
<string name="header_raw_ad_records">Raw Ad Records</string>
<string name="header_scan_record">Scan Record</string>
<string name="header_rssi_info">RSSI Info</string>
<string name="header_device_info">Device Info</string>
<string name="invalid_device_data">Invalid Device Data!</string>
<string name="unknown">unknown</string>
</resources>
+22 -2
View File
@@ -1,4 +1,4 @@
<resources>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!--
Base application theme, dependent on API level. This theme is replaced
@@ -17,4 +17,24 @@
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
</resources>
<style name="GridLayoutDataTextView" parent="android:Widget.Holo.Light.TextView">
<item name="android:textSize">12sp</item>
<item name="android:layout_weight">1</item>
<item name="android:ellipsize">none</item>
<item name="android:singleLine">false</item>
<item name="android:scrollHorizontally">false</item>
<item name="android:maxLines">100</item>
<item name="android:inputType">textMultiLine</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">match_parent</item>
</style>
<style name="GridLayoutDataTextViewMonospace" parent="@style/GridLayoutDataTextView">
<item name="android:typeface">monospace</item>
</style>
<style name="GridLayoutTitleTextView" parent="android:Widget.Holo.Light.TextView">
<item name="android:textSize">12sp</item>
</style>
</resources>
@@ -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<AdRecord> 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);
}
}