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
+14 -13
View File
@@ -1,43 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
package="uk.co.alt236.btlescan"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="3"
android:versionName="0.0.3" >
android:versionName="0.0.3">
<uses-sdk
android:minSdkVersion="18"
android:targetSdkVersion="18" />
android:targetSdkVersion="18"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />
android:required="false"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
android:theme="@style/AppTheme">
<activity
android:name="uk.co.alt236.btlescan.activities.MainActivity"
android:label="@string/app_name" >
android:label="@string/app_name">
<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>
</activity>
<activity
android:name="uk.co.alt236.btlescan.activities.DeviceDetailsActivity"
android:label="@string/app_name" >
android:label="@string/app_name">
</activity>
<activity android:name="uk.co.alt236.btlescan.activities.DeviceControlActivity" />
<activity android:name="uk.co.alt236.btlescan.activities.DeviceControlActivity"/>
<service
android:name="uk.co.alt236.btlescan.services.BluetoothLeService"
android:enabled="true" />
android:enabled="true"/>
</application>
</manifest>
@@ -16,16 +16,6 @@
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.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
@@ -44,8 +34,19 @@ import android.view.View;
import android.widget.ExpandableListView;
import android.widget.SimpleExpandableListAdapter;
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.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,
@@ -54,358 +55,356 @@ import butterknife.InjectView;
* Bluetooth LE API.
*/
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";
private static final String LIST_UUID = "UUID";
@Override
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;
private BluetoothLeService mBluetoothLeService;
private List<List<BluetoothGattCharacteristic>> mGattCharacteristics = new ArrayList<List<BluetoothGattCharacteristic>>();
private String mDeviceAddress;
private String mDeviceName;
mGattUUID.setText(tryString(uuid, noData));
mGattUUIDDesc.setText(GattAttributeResolver.getAttributeName(uuid, getString(R.string.unknown)));
mDataAsArray.setText(ByteUtils.byteArrayToHexString(dataArr));
mDataAsString.setText(new String(dataArr));
}
}
};
@InjectView(R.id.gatt_services_list) ExpandableListView mGattServicesList;
@InjectView(R.id.connection_state) TextView mConnectionState;
private void clearUI() {
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;
@InjectView(R.id.description) TextView mGattUUIDDesc;
@InjectView(R.id.data_as_string) TextView mDataAsString;
@InjectView(R.id.data_as_array) TextView mDataAsArray;
// Demonstrates how to iterate through the supported GATT Services/Characteristics.
// In this sample, we populate the data structure that is bound to the ExpandableListView
// on the UI.
private void displayGattServices(final List<BluetoothGattService> gattServices) {
if (gattServices == null) return;
generateExportString(gattServices);
private boolean mConnected = false;
private String mExportString;
String uuid = null;
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.
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);
}
// Loops through available GATT Services.
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);
@Override
public void onServiceDisconnected(final ComponentName componentName) {
mBluetoothLeService = null;
}
};
final List<Map<String, String>> gattCharacteristicGroupData = new ArrayList<Map<String, String>>();
final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
final List<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>();
// 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);
// Loops through available Characteristics.
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
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);
}
mGattUUID.setText(tryString(uuid, noData));
mGattUUIDDesc.setText(GattAttributeResolver.getAttributeName(uuid, getString(R.string.unknown)));
mDataAsArray.setText(ByteUtils.byteArrayToHexString(dataArr));
mDataAsString.setText(new String(dataArr));
}
}
};
mGattCharacteristics.add(charas);
gattCharacteristicData.add(gattCharacteristicGroupData);
}
// 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;
}
};
final SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
this,
gattServiceData,
android.R.layout.simple_expandable_list_item_2,
new String[]{LIST_NAME, LIST_UUID},
new int[]{android.R.id.text1, android.R.id.text2},
gattCharacteristicData,
android.R.layout.simple_expandable_list_item_2,
new String[]{LIST_NAME, LIST_UUID},
new int[]{android.R.id.text1, android.R.id.text2}
);
private void clearUI() {
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);
}
mGattServicesList.setAdapter(gattServiceAdapter);
invalidateOptionsMenu();
}
private void generateExportString(final List<BluetoothGattService> gattServices){
final String unknownServiceString = getResources().getString(R.string.unknown_service);
final String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
final StringBuilder exportBuilder = new StringBuilder();
private void generateExportString(final List<BluetoothGattService> gattServices) {
final String unknownServiceString = getResources().getString(R.string.unknown_service);
final String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
final StringBuilder exportBuilder = new StringBuilder();
exportBuilder.append("Device Name: ");
exportBuilder.append(mDeviceName);
exportBuilder.append('\n');
exportBuilder.append("Device Address: ");
exportBuilder.append(mDeviceAddress);
exportBuilder.append('\n');
exportBuilder.append('\n');
exportBuilder.append("Device Name: ");
exportBuilder.append(mDeviceName);
exportBuilder.append('\n');
exportBuilder.append("Device Address: ");
exportBuilder.append(mDeviceAddress);
exportBuilder.append('\n');
exportBuilder.append('\n');
exportBuilder.append("Services:");
exportBuilder.append("--------------------------");
exportBuilder.append('\n');
exportBuilder.append("Services:");
exportBuilder.append("--------------------------");
exportBuilder.append('\n');
String uuid = null;
for (final BluetoothGattService gattService : gattServices) {
uuid = gattService.getUuid().toString();
String uuid = null;
for (final BluetoothGattService gattService : gattServices) {
uuid = gattService.getUuid().toString();
exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownServiceString));
exportBuilder.append(" (");
exportBuilder.append(uuid);
exportBuilder.append(')');
exportBuilder.append('\n');
exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownServiceString));
exportBuilder.append(" (");
exportBuilder.append(uuid);
exportBuilder.append(')');
exportBuilder.append('\n');
final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
uuid = gattCharacteristic.getUuid().toString();
final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
uuid = gattCharacteristic.getUuid().toString();
exportBuilder.append('\t');
exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownCharaString));
exportBuilder.append(" (");
exportBuilder.append(uuid);
exportBuilder.append(')');
exportBuilder.append('\n');
}
exportBuilder.append('\t');
exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownCharaString));
exportBuilder.append(" (");
exportBuilder.append(uuid);
exportBuilder.append(')');
exportBuilder.append('\n');
}
exportBuilder.append('\n');
exportBuilder.append('\n');
}
exportBuilder.append('\n');
exportBuilder.append('\n');
}
exportBuilder.append("--------------------------");
exportBuilder.append('\n');
exportBuilder.append("--------------------------");
exportBuilder.append('\n');
mExportString = exportBuilder.toString();
}
mExportString = exportBuilder.toString();
}
// Demonstrates how to iterate through the supported GATT Services/Characteristics.
// In this sample, we populate the data structure that is bound to the ExpandableListView
// on the UI.
private void displayGattServices(final List<BluetoothGattService> gattServices) {
if (gattServices == null) return;
generateExportString(gattServices);
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gatt_services);
String uuid = null;
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>>();
final Intent intent = getIntent();
final BluetoothLeDevice device = intent.getParcelableExtra(EXTRA_DEVICE);
mDeviceName = device.getName();
mDeviceAddress = device.getAddress();
// Loops through available GATT Services.
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);
ButterKnife.inject(this);
final List<Map<String, String>> gattCharacteristicGroupData = new ArrayList<Map<String, String>>();
final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
final List<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>();
// Sets up UI references.
((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
mGattServicesList.setOnChildClickListener(servicesListClickListner);
// Loops through available Characteristics.
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
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);
}
getActionBar().setTitle(mDeviceName);
getActionBar().setDisplayHomeAsUpEnabled(true);
mGattCharacteristics.add(charas);
gattCharacteristicData.add(gattCharacteristicGroupData);
}
final Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}
final SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
this,
gattServiceData,
android.R.layout.simple_expandable_list_item_2,
new String[] {LIST_NAME, LIST_UUID},
new int[] { android.R.id.text1, android.R.id.text2 },
gattCharacteristicData,
android.R.layout.simple_expandable_list_item_2,
new String[] {LIST_NAME, LIST_UUID},
new int[] { android.R.id.text1, android.R.id.text2 }
);
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.gatt_services, menu);
if (mConnected) {
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);
}
mGattServicesList.setAdapter(gattServiceAdapter);
invalidateOptionsMenu();
}
if (mExportString == null) {
menu.findItem(R.id.menu_share).setVisible(false);
} else {
menu.findItem(R.id.menu_share).setVisible(true);
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gatt_services);
return true;
}
final Intent intent = getIntent();
final BluetoothLeDevice device = intent.getParcelableExtra(EXTRA_DEVICE);
mDeviceName = device.getName();
mDeviceAddress = device.getAddress();
@Override
protected void onDestroy() {
super.onDestroy();
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.
((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
mGattServicesList.setOnChildClickListener(servicesListClickListner);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
intent.putExtra(android.content.Intent.EXTRA_TEXT, mExportString);
getActionBar().setTitle(mDeviceName);
getActionBar().setDisplayHomeAsUpEnabled(true);
startActivity(Intent.createChooser(
intent,
getString(R.string.exporter_email_device_list_picker_text)));
final Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.gatt_services, menu);
if (mConnected) {
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);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
}
if(mExportString == null){
menu.findItem(R.id.menu_share).setVisible(false);
} else {
menu.findItem(R.id.menu_share).setVisible(true);
}
@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);
}
}
return true;
}
private void updateConnectionState(final int resourceId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
final int colourId;
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
mBluetoothLeService = null;
}
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;
}
@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);
mConnectionState.setText(resourceId);
mConnectionState.setTextColor(getResources().getColor(colourId));
}
});
}
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
intent.putExtra(android.content.Intent.EXTRA_TEXT, mExportString);
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;
}
startActivity(Intent.createChooser(
intent,
getString(R.string.exporter_email_device_list_picker_text)));
return true;
}
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;
}
}
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;
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.Locale;
import butterknife.ButterKnife;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
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.btlescan.R;
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{
public static final String EXTRA_DEVICE = "extra_device";
private BluetoothLeDevice mDevice;
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){
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()) + "'");
tvTitle.setText(title );
tvString.setText("'" + AdRecordUtils.getRecordDataAsString(record) + "'");
tvArray.setText("'" + ByteUtils.byteArrayToHexString(record.getData()) + "'");
adapter.addView(lt);
}
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){
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());
tvName.setText(device.getName());
tvAddress.setText(device.getAddress());
tvClass.setText(device.getBluetoothDeviceClassName());
tvBondingState.setText(device.getBluetoothDeviceBondState());
adapter.addView(lt);
}
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){
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);
}
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){
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());
tvMajor.setText(iBeaconData.getMajor() + " (" + hexEncode(iBeaconData.getMajor()) + ")");
tvMinor.setText(iBeaconData.getMinor() + " (" + hexEncode(iBeaconData.getMinor()) + ")");
tvTxPower.setText(iBeaconData.getCalibratedTxPower() + " (" + hexEncode(iBeaconData.getCalibratedTxPower()) + ")");
tvCompanyId.setText(
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);
}
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){
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()));
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);
}
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){
appendSimpleText(adapter, ByteUtils.byteArrayToHexString(data));
}
private void appendSimpleText(final MergeAdapter adapter, final String 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){
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);
tvData.setText(data);
adapter.addView(lt);
}
adapter.addView(lt);
}
private String formatRssi(final double rssi){
return getString(R.string.formatter_db, String.valueOf(rssi));
}
private String formatRssi(final double rssi) {
return getString(R.string.formatter_db, String.valueOf(rssi));
}
private String formatRssi(final int rssi){
return getString(R.string.formatter_db, String.valueOf(rssi));
}
private String formatRssi(final int rssi) {
return getString(R.string.formatter_db, String.valueOf(rssi));
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
ButterKnife.inject(this);
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
ButterKnife.inject(this);
mDevice = getIntent().getParcelableExtra(EXTRA_DEVICE);
mDevice = getIntent().getParcelableExtra(EXTRA_DEVICE);
pupulateDetails(mDevice);
}
pupulateDetails(mDevice);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.details, menu);
return true;
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.details, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_connect:
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_connect:
final Intent intent = new Intent(this, DeviceControlActivity.class);
intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, mDevice);
final Intent intent = new Intent(this, DeviceControlActivity.class);
intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, mDevice);
startActivity(intent);
startActivity(intent);
break;
}
return true;
}
break;
}
return true;
}
private void pupulateDetails(final BluetoothLeDevice device) {
final MergeAdapter adapter = new MergeAdapter();
private void pupulateDetails(final BluetoothLeDevice device) {
final MergeAdapter adapter = new MergeAdapter();
if(device == null){
appendHeader(adapter, getString(R.string.header_device_info));
appendSimpleText(adapter, getString(R.string.invalid_device_data));
} else {
appendHeader(adapter, getString(R.string.header_device_info));
appendDeviceInfo(adapter, device);
if (device == null) {
appendHeader(adapter, getString(R.string.header_device_info));
appendSimpleText(adapter, getString(R.string.invalid_device_data));
} else {
appendHeader(adapter, getString(R.string.header_device_info));
appendDeviceInfo(adapter, device);
appendHeader(adapter, getString(R.string.header_rssi_info));
appendRssiInfo(adapter, device);
appendHeader(adapter, getString(R.string.header_rssi_info));
appendRssiInfo(adapter, device);
appendHeader(adapter, getString(R.string.header_scan_record));
appendSimpleText(adapter, device.getScanRecord());
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));
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){
for (final AdRecord record : adRecords) {
appendAdRecordView(
adapter,
"#" + record.getType() + " " + record.getHumanReadableType(),
record);
}
}
appendAdRecordView(
adapter,
"#" + record.getType() + " " + record.getHumanReadableType(),
record);
}
}
final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device);
if(isIBeacon){
final IBeaconManufacturerData iBeaconData = new IBeaconManufacturerData(device);
appendHeader(adapter, getString(R.string.header_ibeacon_data));
appendIBeaconInfo(adapter, iBeaconData);
}
final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device);
if (isIBeacon) {
final IBeaconManufacturerData iBeaconData = new IBeaconManufacturerData(device);
appendHeader(adapter, getString(R.string.header_ibeacon_data));
appendIBeaconInfo(adapter, iBeaconData);
}
}
getListView().setAdapter(adapter);
}
}
getListView().setAdapter(adapter);
}
private static String formatTime(final long time){
return TimeFormatter.getIsoDateTime(time);
}
private static String formatTime(final long time) {
return TimeFormatter.getIsoDateTime(time);
}
private static String hexEncode(final int integer){
return "0x" + Integer.toHexString(integer).toUpperCase(Locale.US);
}
private static String hexEncode(final int integer) {
return "0x" + Integer.toHexString(integer).toUpperCase(Locale.US);
}
}
@@ -1,12 +1,5 @@
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.ListActivity;
import android.bluetooth.BluetoothAdapter;
@@ -22,174 +15,186 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import butterknife.ButterKnife;
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 {
@InjectView(R.id.tvBluetoothLe) TextView mTvBluetoothLeStatus;
@InjectView(R.id.tvBluetoothStatus) TextView mTvBluetoothStatus;
@InjectView(R.id.tvItemCount) TextView mTvItemCount;
@InjectView(R.id.tvBluetoothLe)
TextView mTvBluetoothLeStatus;
@InjectView(R.id.tvBluetoothStatus)
TextView mTvBluetoothStatus;
@InjectView(R.id.tvItemCount)
TextView mTvItemCount;
private BluetoothUtils mBluetoothUtils;
private BluetoothLeScanner mScanner;
private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothLeDeviceStore mDeviceStore;
private BluetoothUtils mBluetoothUtils;
private BluetoothLeScanner mScanner;
private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothLeDeviceStore mDeviceStore;
private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
final BluetoothLeDevice deviceLe = new BluetoothLeDevice(device, rssi, scanRecord, System.currentTimeMillis());
mDeviceStore.addDevice(deviceLe);
final EasyObjectCursor<BluetoothLeDevice> c = mDeviceStore.getDeviceCursor();
final BluetoothLeDevice deviceLe = new BluetoothLeDevice(device, rssi, scanRecord, System.currentTimeMillis());
mDeviceStore.addDevice(deviceLe);
final EasyObjectCursor<BluetoothLeDevice> c = mDeviceStore.getDeviceCursor();
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.swapCursor(c);
updateItemCount(mLeDeviceListAdapter.getCount());
}
});
}
};
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.swapCursor(c);
updateItemCount(mLeDeviceListAdapter.getCount());
}
});
}
};
private void updateItemCount(final int count){
mTvItemCount.setText(
getString(
R.string.formatter_item_count,
String.valueOf(count)));
}
private void displayAboutDialog() {
// REALLY REALLY LAZY LINKIFIED DIALOG
final int paddingSizeDp = 5;
final float scale = getResources().getDisplayMetrics().density;
final int dpAsPixels = (int) (paddingSizeDp * scale + 0.5f);
private void displayAboutDialog(){
// REALLY REALLY LAZY LINKIFIED DIALOG
final int paddingSizeDp = 5;
final float scale = getResources().getDisplayMetrics().density;
final int dpAsPixels = (int) (paddingSizeDp * scale + 0.5f);
final TextView textView = new TextView(this);
final SpannableString text = new SpannableString(getString(R.string.about_dialog_text));
final TextView textView=new TextView(this);
final SpannableString text = new SpannableString(getString(R.string.about_dialog_text));
textView.setText(text);
textView.setAutoLinkMask(RESULT_OK);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setPadding(dpAsPixels, dpAsPixels, dpAsPixels, dpAsPixels);
textView.setText(text);
textView.setAutoLinkMask(RESULT_OK);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setPadding(dpAsPixels, dpAsPixels, dpAsPixels, dpAsPixels);
Linkify.addLinks(text, Linkify.ALL);
new AlertDialog.Builder(this)
.setTitle(R.string.menu_about)
.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);
new AlertDialog.Builder(this)
.setTitle(R.string.menu_about)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int id) {}
})
.setView(textView)
.show();
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
mDeviceStore = new BluetoothLeDeviceStore();
mBluetoothUtils = new BluetoothUtils(this);
mScanner = new BluetoothLeScanner(mLeScanCallback, mBluetoothUtils);
updateItemCount(0);
}
mDeviceStore = new BluetoothLeDeviceStore();
mBluetoothUtils = new BluetoothUtils(this);
mScanner = new BluetoothLeScanner(mLeScanCallback, mBluetoothUtils);
updateItemCount(0);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
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
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
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);
}
if (getListView().getCount() > 0) {
menu.findItem(R.id.menu_share).setVisible(true);
} else {
menu.findItem(R.id.menu_share).setVisible(false);
}
if(getListView().getCount() > 0){
menu.findItem(R.id.menu_share).setVisible(true);
} else {
menu.findItem(R.id.menu_share).setVisible(false);
}
return true;
}
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
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;
final Intent intent = new Intent(this, DeviceDetailsActivity.class);
intent.putExtra(DeviceDetailsActivity.EXTRA_DEVICE, device);
final Intent intent = new Intent(this, DeviceDetailsActivity.class);
intent.putExtra(DeviceDetailsActivity.EXTRA_DEVICE, device);
startActivity(intent);
}
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
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
protected void onPause() {
super.onPause();
mScanner.scanLeDevice(-1, false);
}
@Override
protected void onPause() {
super.onPause();
mScanner.scanLeDevice(-1, false);
}
@Override
public void onResume() {
super.onResume();
final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn();
final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported();
@Override
public void onResume(){
super.onResume();
final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn();
final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported();
if (mIsBluetoothOn) {
mTvBluetoothStatus.setText(R.string.on);
} else {
mTvBluetoothStatus.setText(R.string.off);
}
if(mIsBluetoothOn){
mTvBluetoothStatus.setText(R.string.on);
} else {
mTvBluetoothStatus.setText(R.string.off);
}
if (mIsBluetoothLePresent) {
mTvBluetoothLeStatus.setText(R.string.supported);
} else {
mTvBluetoothLeStatus.setText(R.string.not_supported);
}
if(mIsBluetoothLePresent){
mTvBluetoothLeStatus.setText(R.string.supported);
} else {
mTvBluetoothLeStatus.setText(R.string.not_supported);
}
invalidateOptionsMenu();
}
invalidateOptionsMenu();
}
private void startScan() {
final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn();
final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported();
mDeviceStore.clear();
updateItemCount(0);
private void startScan(){
final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn();
final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported();
mDeviceStore.clear();
updateItemCount(0);
mLeDeviceListAdapter = new LeDeviceListAdapter(this, mDeviceStore.getDeviceCursor());
setListAdapter(mLeDeviceListAdapter);
mLeDeviceListAdapter = new LeDeviceListAdapter(this, mDeviceStore.getDeviceCursor());
setListAdapter(mLeDeviceListAdapter);
mBluetoothUtils.askUserToEnableBluetoothIfNeeded();
if (mIsBluetoothOn && mIsBluetoothLePresent) {
mScanner.scanLeDevice(-1, true);
invalidateOptionsMenu();
}
}
mBluetoothUtils.askUserToEnableBluetoothIfNeeded();
if(mIsBluetoothOn && mIsBluetoothLePresent){
mScanner.scanLeDevice(-1, true);
invalidateOptionsMenu();
}
}
private void updateItemCount(final int count) {
mTvItemCount.setText(
getString(
R.string.formatter_item_count,
String.valueOf(count)));
}
}
@@ -1,11 +1,5 @@
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.support.v4.widget.SimpleCursorAdapter;
import android.view.LayoutInflater;
@@ -13,111 +7,119 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
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.
public class LeDeviceListAdapter extends SimpleCursorAdapter {
private final LayoutInflater mInflator;
private final Activity mActivity;
private final LayoutInflater mInflator;
private final Activity mActivity;
public LeDeviceListAdapter(final Activity activity, final EasyObjectCursor<BluetoothLeDevice> cursor) {
super(activity, R.layout.list_item_device, cursor, new String[0], new int[0], 0);
mInflator = activity.getLayoutInflater();
mActivity = activity;
}
public LeDeviceListAdapter(final Activity activity, final EasyObjectCursor<BluetoothLeDevice> cursor) {
super(activity, R.layout.list_item_device, cursor, new String[0], new int[0], 0);
mInflator = activity.getLayoutInflater();
mActivity = activity;
}
@SuppressWarnings("unchecked")
@Override
public EasyObjectCursor<BluetoothLeDevice> getCursor(){
return ((EasyObjectCursor<BluetoothLeDevice>) super.getCursor());
}
@SuppressWarnings("unchecked")
@Override
public EasyObjectCursor<BluetoothLeDevice> getCursor() {
return ((EasyObjectCursor<BluetoothLeDevice>) super.getCursor());
}
@Override
public BluetoothLeDevice getItem(final int i){
return getCursor().getItem(i);
}
@Override
public BluetoothLeDevice getItem(final int i) {
return getCursor().getItem(i);
}
@Override
public long getItemId(final int i) {
return i;
}
@Override
public long getItemId(final int i) {
return i;
}
@Override
public View getView(final int i, View view, final ViewGroup viewGroup) {
final ViewHolder viewHolder;
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.list_item_device, null);
viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi);
viewHolder.deviceIcon = (ImageView) view.findViewById(R.id.device_icon);
viewHolder.deviceLastUpdated = (TextView) view.findViewById(R.id.device_last_update);
viewHolder.ibeaconMajor = (TextView) view.findViewById(R.id.ibeacon_major);
viewHolder.ibeaconMinor = (TextView) view.findViewById(R.id.ibeacon_minor);
viewHolder.ibeaconDistance = (TextView) view.findViewById(R.id.ibeacon_distance);
viewHolder.ibeaconUUID = (TextView) view.findViewById(R.id.ibeacon_uuid);
viewHolder.ibeaconTxPower = (TextView) view.findViewById(R.id.ibeacon_tx_power);
viewHolder.ibeaconSection = view.findViewById(R.id.ibeacon_section);
viewHolder.ibeaconDistanceDescriptor = (TextView) view.findViewById(R.id.ibeacon_distance_descriptor);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
@Override
public View getView(final int i, View view, final ViewGroup viewGroup) {
final ViewHolder viewHolder;
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.list_item_device, null);
viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi);
viewHolder.deviceIcon = (ImageView) view.findViewById(R.id.device_icon);
viewHolder.deviceLastUpdated = (TextView) view.findViewById(R.id.device_last_update);
viewHolder.ibeaconMajor = (TextView) view.findViewById(R.id.ibeacon_major);
viewHolder.ibeaconMinor = (TextView) view.findViewById(R.id.ibeacon_minor);
viewHolder.ibeaconDistance = (TextView) view.findViewById(R.id.ibeacon_distance);
viewHolder.ibeaconUUID = (TextView) view.findViewById(R.id.ibeacon_uuid);
viewHolder.ibeaconTxPower = (TextView) view.findViewById(R.id.ibeacon_tx_power);
viewHolder.ibeaconSection = view.findViewById(R.id.ibeacon_section);
viewHolder.ibeaconDistanceDescriptor = (TextView) view.findViewById(R.id.ibeacon_distance_descriptor);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
final BluetoothLeDevice device = getCursor().getItem(i);
final String deviceName = device.getName();
final double rssi = device.getRssi();
final BluetoothLeDevice device = getCursor().getItem(i);
final String deviceName = device.getName();
final double rssi = device.getRssi();
if (deviceName != null && deviceName.length() > 0){
viewHolder.deviceName.setText(deviceName);
} else{
viewHolder.deviceName.setText(R.string.unknown_device);
}
if (deviceName != null && deviceName.length() > 0) {
viewHolder.deviceName.setText(deviceName);
} else {
viewHolder.deviceName.setText(R.string.unknown_device);
}
if (IBeaconUtils.isThisAnIBeacon(device)){
final IBeaconDevice iBeacon = new IBeaconDevice(device);
final String accuracy = Constants.DOUBLE_TWO_DIGIT_ACCURACY.format(iBeacon.getAccuracy());
if (IBeaconUtils.isThisAnIBeacon(device)) {
final IBeaconDevice iBeacon = new IBeaconDevice(device);
final String accuracy = Constants.DOUBLE_TWO_DIGIT_ACCURACY.format(iBeacon.getAccuracy());
viewHolder.deviceIcon.setImageResource(R.drawable.ic_device_ibeacon);
viewHolder.ibeaconSection.setVisibility(View.VISIBLE);
viewHolder.ibeaconMajor.setText(String.valueOf(iBeacon.getMajor()));
viewHolder.ibeaconMinor.setText(String.valueOf(iBeacon.getMinor()));
viewHolder.ibeaconTxPower.setText(String.valueOf(iBeacon.getCalibratedTxPower()));
viewHolder.ibeaconUUID.setText(iBeacon.getUUID());
viewHolder.ibeaconDistance.setText(
mActivity.getString(R.string.formatter_meters, accuracy));
viewHolder.ibeaconDistanceDescriptor.setText(iBeacon.getDistanceDescriptor().toString());
} else {
viewHolder.deviceIcon.setImageResource(R.drawable.ic_bluetooth);
viewHolder.ibeaconSection.setVisibility(View.GONE);
}
viewHolder.deviceIcon.setImageResource(R.drawable.ic_device_ibeacon);
viewHolder.ibeaconSection.setVisibility(View.VISIBLE);
viewHolder.ibeaconMajor.setText(String.valueOf(iBeacon.getMajor()));
viewHolder.ibeaconMinor.setText(String.valueOf(iBeacon.getMinor()));
viewHolder.ibeaconTxPower.setText(String.valueOf(iBeacon.getCalibratedTxPower()));
viewHolder.ibeaconUUID.setText(iBeacon.getUUID());
viewHolder.ibeaconDistance.setText(
mActivity.getString(R.string.formatter_meters, accuracy));
viewHolder.ibeaconDistanceDescriptor.setText(iBeacon.getDistanceDescriptor().toString());
} else {
viewHolder.deviceIcon.setImageResource(R.drawable.ic_bluetooth);
viewHolder.ibeaconSection.setVisibility(View.GONE);
}
final String rssiString =
mActivity.getString(R.string.formatter_db, String.valueOf(rssi));
final String runningAverageRssiString =
mActivity.getString(R.string.formatter_db, String.valueOf(device.getRunningAverageRssi()));
final String rssiString =
mActivity.getString(R.string.formatter_db, String.valueOf(rssi));
final String runningAverageRssiString =
mActivity.getString(R.string.formatter_db, String.valueOf(device.getRunningAverageRssi()));
viewHolder.deviceLastUpdated.setText(
android.text.format.DateFormat.format(
Constants.TIME_FORMAT, new java.util.Date(device.getTimestamp())));
viewHolder.deviceAddress.setText(device.getAddress());
viewHolder.deviceRssi.setText(rssiString + " / " + runningAverageRssiString);
return view;
}
viewHolder.deviceLastUpdated.setText(
android.text.format.DateFormat.format(
Constants.TIME_FORMAT, new java.util.Date(device.getTimestamp())));
viewHolder.deviceAddress.setText(device.getAddress());
viewHolder.deviceRssi.setText(rssiString + " / " + runningAverageRssiString);
return view;
}
static class ViewHolder {
TextView deviceName;
TextView deviceAddress;
TextView deviceRssi;
TextView ibeaconUUID;
TextView ibeaconMajor;
TextView ibeaconMinor;
TextView ibeaconTxPower;
TextView ibeaconDistance;
TextView ibeaconDistanceDescriptor;
TextView deviceLastUpdated;
View ibeaconSection;
ImageView deviceIcon;
}
static class ViewHolder {
TextView deviceName;
TextView deviceAddress;
TextView deviceRssi;
TextView ibeaconUUID;
TextView ibeaconMajor;
TextView ibeaconMinor;
TextView ibeaconTxPower;
TextView ibeaconDistance;
TextView ibeaconDistanceDescriptor;
TextView deviceLastUpdated;
View ibeaconSection;
ImageView deviceIcon;
}
}
@@ -1,5 +1,9 @@
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.FileWriter;
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.TimeFormatter;
import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
public class BluetoothLeDeviceStore {
private final Map<String, BluetoothLeDevice> mDeviceMap;
private final Map<String, BluetoothLeDevice> mDeviceMap;
public BluetoothLeDeviceStore(){
mDeviceMap = new HashMap<String, BluetoothLeDevice>();
}
public BluetoothLeDeviceStore() {
mDeviceMap = new HashMap<String, BluetoothLeDevice>();
}
public void addDevice(final BluetoothLeDevice device){
if(mDeviceMap.containsKey(device.getAddress())){
mDeviceMap.get(device.getAddress()).updateRssiReading(device.getTimestamp(), device.getRssi());
} else {
mDeviceMap.put(device.getAddress(), device);
}
}
public void addDevice(final BluetoothLeDevice device) {
if (mDeviceMap.containsKey(device.getAddress())) {
mDeviceMap.get(device.getAddress()).updateRssiReading(device.getTimestamp(), device.getRssi());
} else {
mDeviceMap.put(device.getAddress(), device);
}
}
public void clear(){
mDeviceMap.clear();
}
public void 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){
FileWriter writer = null;
try {
writer = new FileWriter(file);
writer.append(contents);
writer.flush();
public List<BluetoothLeDevice> getDeviceList() {
final List<BluetoothLeDevice> methodResult = new ArrayList<BluetoothLeDevice>(mDeviceMap.values());
} catch (final IOException e) {
e.printStackTrace();
}finally{
try {
writer.close();
} catch (final IOException e) {
e.printStackTrace();
}
}
return writer;
}
Collections.sort(methodResult, new Comparator<BluetoothLeDevice>() {
public EasyObjectCursor<BluetoothLeDevice> getDeviceCursor(){
return new EasyObjectCursor<BluetoothLeDevice>(
BluetoothLeDevice.class,
getDeviceList(),
"address");
}
@Override
public int compare(final BluetoothLeDevice arg0, final BluetoothLeDevice arg1) {
return arg0.getAddress().compareToIgnoreCase(arg1.getAddress());
}
});
public List<BluetoothLeDevice> getDeviceList(){
final List<BluetoothLeDevice> methodResult = new ArrayList<BluetoothLeDevice>(mDeviceMap.values());
return methodResult;
}
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
public int compare(final BluetoothLeDevice arg0, final BluetoothLeDevice arg1) {
return arg0.getAddress().compareToIgnoreCase(arg1.getAddress());
}
});
for (final BluetoothLeDevice device : list) {
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;
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(){
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');
sb.append('\n');
}
for(final BluetoothLeDevice device : list){
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;
return sb.toString();
}
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 = "";
}
public void shareDataAsEmail(final Context context) {
final long timeInMillis = System.currentTimeMillis();
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));
sb.append('\n');
}
return sb.toString();
}
public void shareDataAsEmail(final Context context){
final long timeInMillis = System.currentTimeMillis();
final String to = null;
final String to = null;
final String subject = context.getString(
R.string.exporter_email_device_list_subject,
TimeFormatter.getIsoDateTime(timeInMillis));
R.string.exporter_email_device_list_subject,
TimeFormatter.getIsoDateTime(timeInMillis));
final String message = context.getString(R.string.exporter_email_device_list_body);
final Intent i = new Intent(Intent.ACTION_SEND);
i.setType("plain/text");
try {
final File outputDir = context.getCacheDir();
final File outputFile = File.createTempFile("bluetooth_le_" + timeInMillis, ".csv", outputDir);
outputFile.setReadable(true, false);
final File outputDir = context.getCacheDir();
final File outputFile = File.createTempFile("bluetooth_le_" + timeInMillis, ".csv", outputDir);
outputFile.setReadable(true, false);
generateFile(outputFile, getListAsCsv());
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_TEXT, message);
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) {
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;
import java.util.List;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -33,247 +31,243 @@ import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import java.util.List;
/**
* Service for managing connection and data communication with a GATT server hosted on a
* given Bluetooth LE device.
*/
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;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
private BluetoothGatt mBluetoothGatt;
private int mConnectionState = STATE_DISCONNECTED;
@Override
public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
@Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
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";
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";
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
// 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 onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
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());
@Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
};
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
@Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
intent.putExtra(EXTRA_UUID_CHAR, characteristic.getUuid().toString());
@Override
public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
// Always try to add the RAW value
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
intent.putExtra(EXTRA_DATA_RAW, data);
}
@Override
public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
sendBroadcast(intent);
}
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
/**
* 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;
}
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
intent.putExtra(EXTRA_UUID_CHAR, characteristic.getUuid().toString());
/**
* Connects to the GATT server hosted on the Bluetooth LE device.
*
* @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
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
intent.putExtra(EXTRA_DATA_RAW, data);
}
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null
&& address.equals(mBluetoothDeviceAddress)
&& 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 {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
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) {
return mBinder;
}
/**
* Disconnects an existing connection or cancel a pending connection. The disconnection result
* 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) {
// After using a given device, you should make sure that BluetoothGatt.close() is called
// 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);
}
/**
* 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;
private final IBinder mBinder = new LocalBinder();
return mBluetoothGatt.getServices();
}
/**
* Initializes a reference to the local Bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
/**
* Initializes a reference to the local Bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
return true;
}
/**
* Connects to the GATT server hosted on the Bluetooth LE device.
*
* @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;
}
@Override
public IBinder onBind(final Intent intent) {
return mBinder;
}
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null
&& address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null) {
@Override
public boolean onUnbind(final Intent intent) {
// After using a given device, you should make sure that BluetoothGatt.close() is called
// 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()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
/**
* 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);
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
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;
}
/**
* 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);
}
/**
* Disconnects an existing connection or cancel a pending connection. The disconnection result
* 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();
}
/**
* 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();
}
public class LocalBinder extends Binder {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
}
@@ -5,40 +5,42 @@ import android.os.Handler;
import android.util.Log;
public class BluetoothLeScanner {
private final Handler mHandler;
private final BluetoothAdapter.LeScanCallback mLeScanCallback;
private final BluetoothUtils mBluetoothUtils;
private boolean mScanning;
public BluetoothLeScanner(final BluetoothAdapter.LeScanCallback leScanCallback, final BluetoothUtils bluetoothUtils){
mHandler = new Handler();
mLeScanCallback = leScanCallback;
mBluetoothUtils = bluetoothUtils;
}
public boolean isScanning() {
return mScanning;
}
private final Handler mHandler;
private final BluetoothAdapter.LeScanCallback mLeScanCallback;
private final BluetoothUtils mBluetoothUtils;
private boolean 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(mScanning){return;}
Log.d("TAG", "~ Starting Scan");
if (mScanning) {
return;
}
Log.d("TAG", "~ Starting Scan");
// Stops scanning after a pre-defined scan period.
if(duration > 0){
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Log.d("TAG", "~ Stopping Scan (timeout)");
mScanning = false;
mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback);
}
}, duration);
}
if (duration > 0) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Log.d("TAG", "~ Stopping Scan (timeout)");
mScanning = false;
mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback);
}
}, duration);
}
mScanning = true;
mBluetoothUtils.getBluetoothAdapter().startLeScan(mLeScanCallback);
} else {
Log.d("TAG", "~ Stopping Scan");
Log.d("TAG", "~ Stopping Scan");
mScanning = false;
mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback);
}
@@ -8,37 +8,36 @@ import android.content.Intent;
import android.content.pm.PackageManager;
public final class BluetoothUtils {
private final Activity mActivity;
private final BluetoothAdapter mBluetoothAdapter;
public final static int REQUEST_ENABLE_BT = 2001;
private final Activity mActivity;
private final BluetoothAdapter mBluetoothAdapter;
public final static int REQUEST_ENABLE_BT = 2001;
public BluetoothUtils(final Activity activity){
mActivity = activity;
final BluetoothManager btManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = btManager.getAdapter();
}
public void askUserToEnableBluetoothIfNeeded(){
if (isBluetoothLeSupported() && (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())) {
final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
mActivity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
public BluetoothAdapter getBluetoothAdapter(){
return mBluetoothAdapter;
}
public boolean isBluetoothLeSupported(){
return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
public boolean isBluetoothOn(){
if (mBluetoothAdapter == null) {
return false;
} else {
return mBluetoothAdapter.isEnabled();
}
}
public BluetoothUtils(final Activity activity) {
mActivity = activity;
final BluetoothManager btManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = btManager.getAdapter();
}
public void askUserToEnableBluetoothIfNeeded() {
if (isBluetoothLeSupported() && (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())) {
final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
mActivity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
public BluetoothAdapter getBluetoothAdapter() {
return mBluetoothAdapter;
}
public boolean isBluetoothLeSupported() {
return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
public boolean isBluetoothOn() {
if (mBluetoothAdapter == null) {
return false;
} else {
return mBluetoothAdapter.isEnabled();
}
}
}
@@ -3,6 +3,6 @@ package uk.co.alt236.btlescan.util;
import java.text.DecimalFormat;
public class Constants {
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 DecimalFormat DOUBLE_TWO_DIGIT_ACCURACY = new DecimalFormat("#.##");
public static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
}
@@ -1,23 +1,26 @@
package uk.co.alt236.btlescan.util;
public class CsvWriterHelper {
private static final String QUOTE = "\"";
public static String addStuff(final Integer text){
return QUOTE + text + QUOTE + ",";
}
private static final String QUOTE = "\"";
public static String addStuff(final Long text){
return QUOTE + text + QUOTE + ",";
}
public static String addStuff(final Integer text) {
return QUOTE + text + 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, "'");
public static String addStuff(final Long text) {
return QUOTE + text + 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;
public class TimeFormatter {
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 String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz";
private final static SimpleDateFormat ISO_FORMATTER = new UtcDateFormatter(ISO_FORMAT, Locale.US);
public static String getIsoDateTime(final Date date){
return ISO_FORMATTER.format(date);
}
public static String getIsoDateTime(final Date date) {
return ISO_FORMATTER.format(date);
}
public static String getIsoDateTime(final long millis){
return getIsoDateTime(new Date(millis));
}
public static String getIsoDateTime(final long millis) {
return getIsoDateTime(new Date(millis));
}
}
@@ -1,72 +1,74 @@
/*******************************************************************************
/**
* ****************************************************************************
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* <p/>
* GenieConnect Ltd. ("COMPANY") CONFIDENTIAL
* Unpublished Copyright (c) 2010-2013 GenieConnect Ltd., All Rights Reserved.
*
* <p/>
* NOTICE:
* All information contained herein is, and remains the property of COMPANY.
* 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
* are protected by trade secret or copyright law. Dissemination of this
* information or reproduction of this material is strictly forbidden unless prior
* written permission is obtained from COMPANY. Access to the source code
* contained herein is hereby forbidden to anyone except current COMPANY employees,
* All information contained herein is, and remains the property of COMPANY.
* 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
* are protected by trade secret or copyright law. Dissemination of this
* information or reproduction of this material is strictly forbidden unless prior
* written permission is obtained from COMPANY. Access to the source code
* contained herein is hereby forbidden to anyone except current COMPANY employees,
* managers or contractors who have executed Confidentiality and Non-disclosure
* agreements explicitly covering such access.
*
* The copyright notice above does not evidence any actual or intended publication
* or disclosure of this source code, which includes information that is
* <p/>
* The copyright notice above does not evidence any actual or intended publication
* or disclosure of this source code, which includes information that is
* confidential and/or proprietary, and is a trade secret, of COMPANY.
*
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, OR PUBLIC
* 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
* AND INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE CODE
* 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
* <p/>
* ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, OR PUBLIC
* 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
* AND INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE CODE
* 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
* THAT IT MAY DESCRIBE, IN WHOLE OR IN PART.
******************************************************************************/
* ****************************************************************************
*/
package uk.co.alt236.btlescan.util;
import android.annotation.SuppressLint;
import java.text.DateFormatSymbols;
import java.util.Locale;
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 long serialVersionUID = 1L;
private static final String TIME_ZONE_STRING = "UTC";
private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone(TIME_ZONE_STRING);
@SuppressLint("SimpleDateFormat")
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);
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
*
* (non-Javadoc)
* @see java.text.DateFormat#setTimeZone(java.util.TimeZone)
*/
@Override
public void setTimeZone(final TimeZone timezone){
throw new UnsupportedOperationException("This SimpleDateFormat can only be in " + TIME_ZONE_STRING);
}
private static final String TIME_ZONE_STRING = "UTC";
private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone(TIME_ZONE_STRING);
@SuppressLint("SimpleDateFormat")
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);
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
*
* (non-Javadoc)
* @see java.text.DateFormat#setTimeZone(java.util.TimeZone)
*/
@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"
android:layout_width="56dp"
android:layout_height="wrap_content"
android:minWidth="56dp" >
android:layout_width="56dp"
android:layout_height="wrap_content"
android:minWidth="56dp">
<ProgressBar
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center" />
android:layout_gravity="center"/>
</FrameLayout>
@@ -1,15 +1,14 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
android:layout_height="wrap_content">
</ListView>
</RelativeLayout>
@@ -15,13 +15,13 @@
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<GridLayout
android:id="@+id/deviceInformation"
@@ -29,27 +29,27 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:columnCount="2"
android:useDefaultMargins="true" >
android:useDefaultMargins="true">
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_device_address" />
android:text="@string/label_device_address"/>
<TextView
android:id="@+id/device_address"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_state" />
android:text="@string/label_state"/>
<TextView
android:id="@+id/connection_state"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
</GridLayout>
<View
@@ -57,7 +57,7 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/deviceInformation"
android:background="@android:color/holo_blue_dark" />
android:background="@android:color/holo_blue_dark"/>
<GridLayout
android:id="@+id/gattInformation"
@@ -65,47 +65,47 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:columnCount="2"
android:useDefaultMargins="true" >
android:useDefaultMargins="true">
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_uuid" />
android:text="@string/label_uuid"/>
<TextView
android:id="@+id/uuid"
style="@style/GridLayoutDataTextViewMonospace" />
style="@style/GridLayoutDataTextViewMonospace"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_desc" />
android:text="@string/label_desc"/>
<TextView
android:id="@+id/description"
style="@style/GridLayoutDataTextViewMonospace" />
style="@style/GridLayoutDataTextViewMonospace"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_as_string" />
android:text="@string/label_as_string"/>
<TextView
android:id="@+id/data_as_string"
style="@style/GridLayoutDataTextViewMonospace" />
style="@style/GridLayoutDataTextViewMonospace"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_as_array" />
android:text="@string/label_as_array"/>
<TextView
android:id="@+id/data_as_array"
style="@style/GridLayoutDataTextViewMonospace" />
style="@style/GridLayoutDataTextViewMonospace"/>
</GridLayout>
<View
@@ -113,13 +113,13 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_above="@id/gattInformation"
android:background="@android:color/holo_blue_dark" />
android:background="@android:color/holo_blue_dark"/>
<ExpandableListView
android:id="@+id/gatt_services_list"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_above="@id/lowerSepparator"
android:layout_below="@id/upperSepparator" />
android:layout_below="@id/upperSepparator"/>
</RelativeLayout>
@@ -1,20 +1,20 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<GridLayout
android:id="@+id/gridLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnCount="2"
android:paddingBottom="@dimen/activity_vertical_margin" >
android:paddingBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_column="0"
@@ -22,7 +22,7 @@
android:layout_row="0"
android:text="@string/label_bluetooth_le_status"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/tvBluetoothLe"
@@ -33,7 +33,7 @@
android:layout_row="0"
android:gravity="right"
android:text="@string/not_supported"
android:textSize="12sp" />
android:textSize="12sp"/>
<TextView
android:layout_width="wrap_content"
@@ -43,7 +43,7 @@
android:layout_row="1"
android:text="@string/label_bluetooth_status"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/tvBluetoothStatus"
@@ -54,59 +54,59 @@
android:layout_row="1"
android:gravity="right"
android:text="@string/off"
android:textSize="12sp" />
android:textSize="12sp"/>
</GridLayout>
<View
android:id="@+id/upperSepparator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/holo_blue_dark" />
android:background="@android:color/holo_blue_dark"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
android:orientation="vertical">
<LinearLayout
android:id="@+id/infoContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical" >
android:orientation="vertical">
<View
android:id="@+id/lowerSepparator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/holo_blue_dark" />
android:background="@android:color/holo_blue_dark"/>
<TextView
android:id="@+id/tvItemCount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/formatter_item_count" />
android:text="@string/formatter_item_count"/>
</LinearLayout>
<LinearLayout
android:id="@+id/listContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_above="@id/infoContainer"
android:orientation="vertical" >
android:layout_alignParentTop="true"
android:orientation="vertical">
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"/>
<TextView
android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:text="@string/no_data" />
android:text="@string/no_data"/>
</LinearLayout>
</RelativeLayout>
@@ -15,34 +15,34 @@
limitations under the License.
-->
<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="horizontal" >
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="horizontal">
<ImageView
android:id="@+id/device_icon"
android:paddingTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:src="@drawable/ic_bluetooth" />
android:paddingTop="5dp"
android:src="@drawable/ic_bluetooth"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
android:orientation="vertical">
<TextView
android:id="@+id/device_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp" />
android:textSize="24sp"/>
<GridLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:columnCount="2" >
android:columnCount="2">
<TextView
android:layout_width="wrap_content"
@@ -50,14 +50,14 @@
android:paddingRight="5dp"
android:text="@string/label_mac"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/device_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:typeface="monospace" />
android:typeface="monospace"/>
<TextView
android:layout_width="wrap_content"
@@ -65,14 +65,14 @@
android:paddingRight="5dp"
android:text="Updated:"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/device_last_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:textSize="12sp" />
android:textSize="12sp"/>
<TextView
android:layout_width="wrap_content"
@@ -80,14 +80,14 @@
android:paddingRight="5dp"
android:text="@string/label_rssi"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/device_rssi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:textSize="12sp" />
android:textSize="12sp"/>
</GridLayout>
<GridLayout
@@ -95,7 +95,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/light_gray"
android:columnCount="4" >
android:columnCount="4">
<!-- ROW 1 -->
@@ -105,7 +105,7 @@
android:paddingRight="5dp"
android:text="@string/label_uuid"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/ibeacon_uuid"
@@ -113,7 +113,7 @@
android:layout_height="wrap_content"
android:layout_columnSpan="3"
android:paddingRight="5dp"
android:textSize="12sp" />
android:textSize="12sp"/>
<!-- ROW 2 -->
@@ -123,14 +123,14 @@
android:paddingRight="5dp"
android:text="@string/label_major"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/ibeacon_major"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:textSize="12sp" />
android:textSize="12sp"/>
<TextView
android:layout_width="wrap_content"
@@ -139,14 +139,14 @@
android:paddingRight="5dp"
android:text="@string/label_minor"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/ibeacon_minor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:textSize="12sp" />
android:textSize="12sp"/>
<!-- ROW 3 -->
@@ -156,14 +156,14 @@
android:paddingRight="5dp"
android:text="@string/label_tx_power"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/ibeacon_tx_power"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:textSize="12sp" />
android:textSize="12sp"/>
<TextView
android:layout_width="wrap_content"
@@ -172,14 +172,14 @@
android:paddingRight="5dp"
android:text="@string/label_distance"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/ibeacon_distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:textSize="12sp" />
android:textSize="12sp"/>
<!-- ROW 4 -->
@@ -189,7 +189,7 @@
android:paddingRight="5dp"
android:text="Descriptor:"
android:textSize="12sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/ibeacon_distance_descriptor"
@@ -197,7 +197,7 @@
android:layout_height="wrap_content"
android:layout_columnSpan="3"
android:paddingRight="5dp"
android:textSize="12sp" />
android:textSize="12sp"/>
</GridLayout>
</LinearLayout>
@@ -1,43 +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" >
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" />
android:textStyle="bold"/>
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:useDefaultMargins="true" >
android:useDefaultMargins="true">
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_as_string" />
android:text="@string/label_as_string"/>
<TextView
android:id="@+id/data_as_string"
style="@style/GridLayoutDataTextViewMonospace" />
style="@style/GridLayoutDataTextViewMonospace"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_as_array" />
android:text="@string/label_as_array"/>
<TextView
android:id="@+id/data_as_array"
style="@style/GridLayoutDataTextViewMonospace" />
style="@style/GridLayoutDataTextViewMonospace"/>
</GridLayout>
</LinearLayout>
@@ -1,56 +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" >
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" >
android:useDefaultMargins="true">
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_device_name" />
android:text="@string/label_device_name"/>
<TextView
android:id="@+id/deviceName"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_device_address" />
android:text="@string/label_device_address"/>
<TextView
android:id="@+id/deviceAddress"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_device_class" />
android:text="@string/label_device_class"/>
<TextView
android:id="@+id/deviceClass"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_bonding_state" />
android:text="@string/label_bonding_state"/>
<TextView
android:id="@+id/deviceBondingState"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
</GridLayout>
</LinearLayout>
@@ -1,10 +1,10 @@
<?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" >
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
android:paddingBottom="5dp">
<TextView
android:id="@+id/title"
@@ -13,11 +13,11 @@
android:textAllCaps="true"
android:textColor="@android:color/holo_blue_dark"
android:textSize="14sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/holo_blue_dark" />
android:background="@android:color/holo_blue_dark"/>
</LinearLayout>
@@ -1,76 +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" >
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" >
android:useDefaultMargins="true">
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_company_id" />
android:text="@string/label_company_id"/>
<TextView
android:id="@+id/companyId"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_advertisement" />
android:text="@string/label_advertisement"/>
<TextView
android:id="@+id/advertisement"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_uuid" />
android:text="@string/label_uuid"/>
<TextView
android:id="@+id/uuid"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_major" />
android:text="@string/label_major"/>
<TextView
android:id="@+id/major"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_minor" />
android:text="@string/label_minor"/>
<TextView
android:id="@+id/minor"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_tx_power" />
android:text="@string/label_tx_power"/>
<TextView
android:id="@+id/txpower"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
</GridLayout>
</LinearLayout>
@@ -1,66 +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" >
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" >
android:useDefaultMargins="true">
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_first_timestamp" />
android:text="@string/label_first_timestamp"/>
<TextView
android:id="@+id/firstTimestamp"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_first_rssi" />
android:text="@string/label_first_rssi"/>
<TextView
android:id="@+id/firstRssi"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_last_timestamp" />
android:text="@string/label_last_timestamp"/>
<TextView
android:id="@+id/lastTimestamp"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_last_rssi" />
android:text="@string/label_last_rssi"/>
<TextView
android:id="@+id/lastRssi"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
<TextView
style="@style/GridLayoutTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_running_average_rssi" />
android:text="@string/label_running_average_rssi"/>
<TextView
android:id="@+id/runningAverageRssi"
style="@style/GridLayoutDataTextView" />
style="@style/GridLayoutDataTextView"/>
</GridLayout>
</LinearLayout>
@@ -1,13 +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" >
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" />
style="@style/GridLayoutDataTextViewMonospace"/>
</LinearLayout>
+1 -1
View File
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
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
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
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
android:id="@+id/menu_refresh"
+2 -2
View File
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
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
android:id="@+id/menu_refresh"
@@ -33,9 +33,9 @@
android:title="@string/menu_stop"/>
<item
android:id="@+id/menu_share"
android:icon="@drawable/ic_action_share"
android:orderInCategory="102"
android:showAsAction="ifRoom"
android:icon="@drawable/ic_action_share"
android:title="@string/menu_share"/>
<item
android:id="@+id/menu_about"
+1 -1
View File
@@ -1,3 +1,3 @@
<resources>
<color name="light_gray">#66e0e0e0</color>
<color name="light_gray">#66e0e0e0</color>
</resources>