Currently I am using an Arduino nano to receive data and transmit it to android phone through Bluetooth. I want to set some button on my smart phone, and then after I pressed it, it will transmit a character to arduino.
I have tested my arduino that if I give certain command in serial monitor , it will give me the analog read signal.
I wanna ask how can I transmit a character(like "E") from my phone to arduino?
The following code is associated to bluetooth connection. What else can I add to achieve my goal?
Bluetooth code in arduino
public class Homescreen extends Activity {
private Button mBtnSearch;
private Button mBtnConnect;
private ListView mLstDevices;
private BluetoothAdapter mBTAdapter;
private static final int BT_ENABLE_REQUEST = 10; // This is the code we use for BT Enable
private static final int SETTINGS = 20;
private UUID mDeviceUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // Standard SPP UUID
// (http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureRfcommSocketToServiceRecord%28java.util.UUID%29)
private int mBufferSize = 50000; //Default
public static final String DEVICE_EXTRA = "com.blueserial.SOCKET";
public static final String DEVICE_UUID = "com.blueserial.uuid";
private static final String DEVICE_LIST = "com.blueserial.devicelist";
private static final String DEVICE_LIST_SELECTED = "com.blueserial.devicelistselected";
public static final String BUFFER_SIZE = "com.blueserial.buffersize";
private static final String TAG = "BlueTest5-Homescreen";
public String isECG;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_homescreen);
ActivityHelper.initialize(this); //This is to ensure that the rotation persists across activities and not just this one
Log.d(TAG, "Created");
Intent i = getIntent();
if (i.hasExtra("isECG")){
isECG = i.getStringExtra("isECG");
}
TextView tv1 = (TextView)findViewById(R.id.tv1);
tv1.setText(isECG);
mBtnSearch = (Button) findViewById(R.id.btnSearch);
mBtnConnect = (Button) findViewById(R.id.btnConnect);
mLstDevices = (ListView) findViewById(R.id.lstDevices);
Button btBack = (Button) findViewById(R.id.btBack);
btBack.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(Homescreen.this, StartScreen.class);
startActivity(intent);
}
});
/*
*Check if there is a savedInstanceState. If yes, that means the onCreate was probably triggered by a configuration change
*like screen rotate etc. If that's the case then populate all the views that are necessary here
*/
if (savedInstanceState != null) {
ArrayList<BluetoothDevice> list = savedInstanceState.getParcelableArrayList(DEVICE_LIST);
if(list!=null){
initList(list);
MyAdapter adapter = (MyAdapter)mLstDevices.getAdapter();
int selectedIndex = savedInstanceState.getInt(DEVICE_LIST_SELECTED);
if(selectedIndex != -1){
adapter.setSelectedIndex(selectedIndex);
mBtnConnect.setEnabled(true);
}
} else {
initList(new ArrayList<BluetoothDevice>());
}
} else {
initList(new ArrayList<BluetoothDevice>());
}
mBtnSearch.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
mBTAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBTAdapter == null) {
Toast.makeText(getApplicationContext(), "Bluetooth not found", Toast.LENGTH_SHORT).show();
} else if (!mBTAdapter.isEnabled()) {
Intent enableBT = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBT, BT_ENABLE_REQUEST);
} else {
new SearchDevices().execute();
}
}
});
mBtnConnect.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
BluetoothDevice device = ((MyAdapter) (mLstDevices.getAdapter())).getSelectedItem();
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.putExtra(DEVICE_EXTRA, device);
intent.putExtra(DEVICE_UUID, mDeviceUUID.toString());
intent.putExtra(BUFFER_SIZE, mBufferSize);
intent.putExtra("isECG", isECG);
intent.setClass(Homescreen.this, MainActivity.class);
startActivity(intent);
}
});
}
/**
* Called when the screen rotates. If this isn't handled, data already generated is no longer available
*/
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
MyAdapter adapter = (MyAdapter) (mLstDevices.getAdapter());
ArrayList<BluetoothDevice> list = (ArrayList<BluetoothDevice>) adapter.getEntireList();
if (list != null) {
outState.putParcelableArrayList(DEVICE_LIST, list);
int selectedIndex = adapter.selectedIndex;
outState.putInt(DEVICE_LIST_SELECTED, selectedIndex);
}
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case BT_ENABLE_REQUEST:
if (resultCode == RESULT_OK) {
msg("Bluetooth Enabled successfully");
new SearchDevices().execute();
} else {
msg("Bluetooth couldn't be enabled");
}
break;
case SETTINGS: //If the settings have been updated
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String uuid = prefs.getString("prefUuid", "Null");
mDeviceUUID = UUID.fromString(uuid);
Log.d(TAG, "UUID: " + uuid);
String bufSize = prefs.getString("prefTextBuffer", "Null");
mBufferSize = Integer.parseInt(bufSize);
String orientation = prefs.getString("prefOrientation", "Null");
Log.d(TAG, "Orientation: " + orientation);
if (orientation.equals("Landscape")) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else if (orientation.equals("Portrait")) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else if (orientation.equals("Auto")) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
}
break;
default:
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* Quick way to call the Toast
* #param str
*/
private void msg(String str) {
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();
}
/**
* Initialize the List adapter
* #param objects
*/
private void initList(List<BluetoothDevice> objects) {
final MyAdapter adapter = new MyAdapter(getApplicationContext(), R.layout.list_item, R.id.lstContent, objects);
mLstDevices.setAdapter(adapter);
mLstDevices.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
adapter.setSelectedIndex(position);
mBtnConnect.setEnabled(true);
}
});
}
/**
* Searches for paired devices. Doesn't do a scan! Only devices which are paired through Settings->Bluetooth
* will show up with this. I didn't see any need to re-build the wheel over here
* #author ryder
*
*/
private class SearchDevices extends AsyncTask<Void, Void, List<BluetoothDevice>> {
#Override
protected List<BluetoothDevice> doInBackground(Void... params) {
Set<BluetoothDevice> pairedDevices = mBTAdapter.getBondedDevices();
List<BluetoothDevice> listDevices = new ArrayList<BluetoothDevice>();
for (BluetoothDevice device : pairedDevices) {
listDevices.add(device);
}
return listDevices;
}
#Override
protected void onPostExecute(List<BluetoothDevice> listDevices) {
super.onPostExecute(listDevices);
if (listDevices.size() > 0) {
MyAdapter adapter = (MyAdapter) mLstDevices.getAdapter();
adapter.replaceItems(listDevices);
} else {
msg("No paired devices found, please pair your serial BT device and try again");
}
}
}
/**
* Custom adapter to show the current devices in the list. This is a bit of an overkill for this
* project, but I figured it would be good learning
* Most of the code is lifted from somewhere but I can't find the link anymore
* #author ryder
*
*/
private class MyAdapter extends ArrayAdapter<BluetoothDevice> {
private int selectedIndex;
private Context context;
private int selectedColor = Color.parseColor("#abcdef");
private List<BluetoothDevice> myList;
public MyAdapter(Context ctx, int resource, int textViewResourceId, List<BluetoothDevice> objects) {
super(ctx, resource, textViewResourceId, objects);
context = ctx;
myList = objects;
selectedIndex = -1;
}
public void setSelectedIndex(int position) {
selectedIndex = position;
notifyDataSetChanged();
}
public BluetoothDevice getSelectedItem() {
return myList.get(selectedIndex);
}
#Override
public int getCount() {
return myList.size();
}
#Override
public BluetoothDevice getItem(int position) {
return myList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
private class ViewHolder {
TextView tv;
}
public void replaceItems(List<BluetoothDevice> list) {
myList = list;
notifyDataSetChanged();
}
public List<BluetoothDevice> getEntireList() {
return myList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
ViewHolder holder;
if (convertView == null) {
vi = LayoutInflater.from(context).inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.tv = (TextView) vi.findViewById(R.id.lstContent);
vi.setTag(holder);
} else {
holder = (ViewHolder) vi.getTag();
}
if (selectedIndex != -1 && position == selectedIndex) {
holder.tv.setBackgroundColor(selectedColor);
} else {
holder.tv.setBackgroundColor(Color.WHITE);
}
BluetoothDevice device = myList.get(position);
holder.tv.setText(device.getName() + "\n " + device.getAddress());
return vi;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.homescreen, menu);
return true;
}
}
in my android code I have these global variables:
BluetoothAdapter mBluetoothAdapter;
BluetoothSocket mmSocket;
BluetoothDevice mmDevice;
OutputStream mmOutputStream;
InputStream mmInputStream;
And then I get my paired bluetooth devices in a similar way to what you did:
#Override
protected List<BluetoothDevice> doInBackground(Void... params) {
Set<BluetoothDevice> pairedDevices = mBTAdapter.getBondedDevices();
List<BluetoothDevice> listDevices = new ArrayList<BluetoothDevice>();
for (BluetoothDevice device : pairedDevices) {
listDevices.add(device);
}
return listDevices;
}
Then when I select the correct bluetooth device, I set it equal to mmDevice:
mmDevice = device;
Then I use this method to open a bluetooth connection:
void openBT() throws IOException{
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); //Standard SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
//Optional
System.out.println("Bluetooth Opened");
Toast.makeText(getApplicationContext(), "Bluetooth Opened", Toast.LENGTH_SHORT).show();
}
The mmOutputStream sends data to the bluetooth module and the mmInputStream reads the incoming data from the bluetooth module. I have this method to send data to the bluetooth module:
void sendData(String m) throws IOException{
String msg = m;
mmOutputStream.write(msg.getBytes());
System.out.println("Data Sent");
}
This converts a string to bytes and sends it to the Arduino. On the Arduino side, you can either say
if(bluetooth.available())
{
char c = bluetooth.read();
Serial.println(c);
}
If you are just reading a single char or say:
if(bluetooth.available())
{
int data = bluetooth.parseInt();
Serial.println(data);
}
To read an integer value that you sent over bluetooth. Hope this helps. If you need anything, don't hesitate to ask.
Related
I have created an adapter to show the scanned devices in my Custom Dialog. The custom dialog appears but even after none of the devices appears.
Whenever I check the logcat there are devices present from Bluetooth Le Scanner.
The code for the custom dialog is below
public class CustomDialog extends Dialog{
private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
private AppCompatButton Scan;
private AppCompatButton Stop;
Context mContext;
TextView textStatus;
BluetoothLeScanner mLeScanner;
private static final int REQUEST_ENABLE_BT = 1;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
ProgressBar contentLoadingProgressBar;
public CustomDialog(#NonNull Context context) {
super(context);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom_dialog);
contentLoadingProgressBar = (ProgressBar) findViewById(R.id.contentLoading);
contentLoadingProgressBar.setVisibility(View.GONE);
textStatus = findViewById(R.id.textStatus);
textStatus.setVisibility(View.GONE);
Scan = (AppCompatButton) findViewById(R.id.scanButton);
Scan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
contentLoadingProgressBar.setVisibility(View.VISIBLE);
Scan.setVisibility(View.GONE);
textStatus.setVisibility(View.VISIBLE);
BlueteethManager.with(getContext()).scanForPeripherals(10000, blueteethDevices -> {
for (BlueteethDevice device : blueteethDevices) {
if (!TextUtils.isEmpty(device.getBluetoothDevice().getName())) {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
Timber.d("%s - %s", device.getName(), device.getMacAddress());
}
}
contentLoadingProgressBar.setVisibility(View.GONE);
textStatus.setVisibility(View.GONE);
Scan.setVisibility(View.VISIBLE);
});
}
});
final BluetoothManager bluetoothManager =
(BluetoothManager) getContext().getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
// Adapter for holding devices found through scanning.
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList<BlueteethDevice> mLeDevices;
private LayoutInflater mInflator;
public LeDeviceListAdapter() {
super();
mLeDevices = new ArrayList<BlueteethDevice>();
mInflator = getLayoutInflater();
}
public void addDevice(BlueteethDevice device) {
if (!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
public void clear() {
mLeDevices.clear();
}
#Override
public int getCount() {
return mLeDevices.size();
}
#Override
public Object getItem(int i) {
return mLeDevices.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
CustomDialog.viewHolder viewHolder;
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.custom_dialog, null);
viewHolder = new CustomDialog.viewHolder();
viewHolder.cardDevice=view.findViewById(R.id.cardDevice);
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
view.setTag(viewHolder);
} else {
viewHolder = (CustomDialog.viewHolder) view.getTag();
}
BlueteethDevice device = mLeDevices.get(i);
final String deviceName = device.getName();
if (deviceName != null && deviceName.length() > 0)
viewHolder.deviceName.setText(deviceName);
else
viewHolder.deviceName.setText("Unknown Device");
viewHolder.deviceAddress.setText(device.getMacAddress());
viewHolder.deviceAddress.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
device.connect(true, isConnected -> {
Timber.d("Is the peripheral connected? %s", Boolean.toString(isConnected));
if (isConnected){
Toast.makeText(mContext, "BLE device Connected", Toast.LENGTH_SHORT).show();
textStatus.setText("Connected");
textStatus.setVisibility(View.VISIBLE);
Scan.setVisibility(View.GONE);
contentLoadingProgressBar.setVisibility(View.GONE);
}
else {
Toast.makeText(mContext, "Failed to connect", Toast.LENGTH_SHORT).show();
}
});
}
});
return view;
}
}
static class viewHolder {
TextView deviceName;
TextView deviceAddress;
CardView cardDevice;
}
}
I am using a third party library for scanning devices. It works correctly though but didn't get any of the device name or address appears on the custom dialog. does the Layout Inflator cause problems to me or what.
This is my code to scan a near by bluetooth device but when i add a custom adapter to it it throwing an error..
public class MainActivity extends Activity {
ListView listDevicesFound;
Button btnScanDevice;
BluetoothAdapter bluetoothAdapter;
int REQUEST_CODE = 1;
MyAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnScanDevice = (Button)findViewById(R.id.scan);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
listDevicesFound = (ListView)findViewById(R.id.list);
listDevicesFound.setAdapter(adapter);
CheckBlueToothState();
btnScanDevice.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
adapter.clear();
if(bluetoothAdapter.startDiscovery())
{
Toast.makeText(getBaseContext(),"Scanning", Toast.LENGTH_LONG).show();
}
}
});
registerReceiver(ActionFoundReceiver,new IntentFilter(BluetoothDevice.ACTION_FOUND));
}
#Override
protected void onDestroy() {
super.onDestroy();unregisterReceiver(ActionFoundReceiver);
}
private void CheckBlueToothState()
{
if (bluetoothAdapter == null)
{
Toast.makeText(getBaseContext(),"Bluetooth Not Supported",Toast.LENGTH_LONG).show();
}
else
{
if (bluetoothAdapter.isEnabled())
{
if(bluetoothAdapter.isDiscovering())
{
Toast.makeText(getBaseContext(),"Bluetooth is currently in device discover process",Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(getBaseContext(),"Bluetooth is Enabled",Toast.LENGTH_LONG).show();
btnScanDevice.setEnabled(true);
}
}
else
{
Toast.makeText(getBaseContext(), "Bluetooth is not Enabled", Toast.LENGTH_LONG).show();
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_CODE);
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUEST_CODE)
{
CheckBlueToothState();
}
}
private final BroadcastReceiver ActionFoundReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
adapter.add(device.getName() + "\n" + device.getAddress());
adapter.notifyDataSetChanged();
}
}};
}
Custom adapter
public class MyAdapter extends ArrayAdapter<BlueDevices>
{
Context context;
int resources;
ArrayList<BlueDevices> Devices = null;
public MyAdapter(Context context, int resource,ArrayList<BlueDevices> Devices) {
super(context, resource, Devices);
this.context=context;
this.resources=resource;
this.Devices=Devices;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = new TextView(getContext());
BlueDevices item = getItem(position);
textView.setTextColor(Color.BLACK);
textView.setText(item.Name+" : "+item.Mac +" date :"+item.getDateFormatted());
return textView;
}
public List<BlueDevices> getAllItem() {
List<BlueDevices> result = new ArrayList<>();
for (int i = 0; i < getCount(); i++) {
Log.e("fef", "getAllItem: " );
result.add(getItem(i));
}
return result;
}
}
BlueDevice:
public class BlueDevices {
public String Name;
public String Mac;
public Date date;
public BlueDevices(String Name)
{
this.Name=Name;
this.Mac=Mac;
this.date=date;
}
private SimpleDateFormat format = new SimpleDateFormat("dd MMMM yyyy");
public String getDateFormatted() {
if (date == null){
return " no date ";
}
return format.format(date);
}
}
this line throwing an error adapter.add(device.getName() + "\n" + device.getAddress());
There are many problems in your code.
Change in Bean file BlueDevices
change constructor to take two arguments.
public BlueDevices(String Name, String Mac)
{
this.Name=Name;
this.Mac=Mac;
}
you no need to set data because you already created method to get data.
change in onCreate method in activity class
In your adapter class constructor take three arguments.
MyAdapter(Context context, int resource,ArrayList<BlueDevices> Devices);
so you have context that get from getApplicationContext(), resource is your layout and Devices is arraylist of your bean class. for that you have to create one layout in your res/layout and one ArrayList with bean typecase.
ArrayList<BlueDevices> list = new ArrayList<>();
add your all devices in to this list using.
list.add(new BlueDevices("Device1","ASDF234SDFSADF"));
list.add(new BlueDevices("Device2","GSDFG34SAF32DF"));
Now create object of adapter class
MyAdapter adapter = new MyAdapter(getApplicationContext(),R.layout.list_items,list);
set this adapter to your ListView using
listview.setAdapter(adapter);
Now create method in adapter class
public BlueDevices getDevices(int position) {return Devices.get(position);}
Add getter method in BlueDevices
public String getName(){
return Name;
}
public String getMac(){
return Mac;
}
Get devices list from adapter in list onItemClickListener
BlueDevices bean = adapter.getDevices(position);
String name = bean.getName();
String mac = bean.getMac();
The app displays the scanned BLE devices. The app screen looks like:
PROBLEM
I want the screen to show a button when the app is opened and below the button should be the text box which will display scanned device.
The code snap is as below:
public class MainActivity extends ListActivity {
private static final String LOG_TAG = "BLEScan";
private LeDeviceListAdapter mLeDeviceListAdapter;
private Handler mHandler;
private boolean mScanning;
private BluetoothAdapter mBluetoothAdapter;
private static final int REQUEST_ENABLE_BT = 1;
private static final long SCAN_PERIOD = 10000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
getActionBar().setTitle(R.string.title_devices);
mHandler = new Handler();
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
return;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
if (!mScanning) {
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_indeterminate_progress);
}
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_scan:
mLeDeviceListAdapter.clear();
scanLeDevice(true);
break;
case R.id.menu_stop:
scanLeDevice(false);
break;
}
return true;
}
#Override
protected void onResume() {
super.onResume();
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
// Initializes list view adapter.
mLeDeviceListAdapter = new LeDeviceListAdapter();
setListAdapter(mLeDeviceListAdapter);
scanLeDevice(true);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// User chose not to enable Bluetooth.
if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
#Override
protected void onPause() {
super.onPause();
scanLeDevice(false);
mLeDeviceListAdapter.clear();
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
if (device == null) return;
}
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
// int ble_value=TypeManufacturerData.class.;
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
static class ViewHolder {
TextView deviceName;
TextView deviceAd;
TextView deviceRssi;
TextView deviceAddress;
}
class DeviceHolder {
BluetoothDevice device;
String additionalData;
int rssi;
public DeviceHolder(BluetoothDevice device, String additionalData, int rssi) {
this.device = device;
this.additionalData = additionalData;
this.rssi = rssi;
}
}
// Adapter for holding devices found through scanning.
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList<BluetoothDevice> mLeDevices;
private ArrayList<DeviceHolder> mLeHolders;
private LayoutInflater mInflator;
public LeDeviceListAdapter() {
super();
mLeDevices = new ArrayList<BluetoothDevice>();
mLeHolders = new ArrayList<DeviceHolder>();
mInflator = MainActivity.this.getLayoutInflater();
}
public void addDevice(DeviceHolder deviceHolder) {
if(!mLeDevices.contains(deviceHolder.device)) {
mLeDevices.add(deviceHolder.device);
mLeHolders.add(deviceHolder);
}
}
public BluetoothDevice getDevice(int position) {
return mLeDevices.get(position);
}
public void clear() {
mLeDevices.clear();
mLeHolders.clear();
}
#Override
public int getCount() {
return mLeDevices.size();
}
#Override
public Object getItem(int i) {
return mLeDevices.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.listitem_device, null);
viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceAd = (TextView) view.findViewById(R.id.device_ad);
viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
BluetoothDevice device = mLeDevices.get(i);
DeviceHolder deviceHolder = mLeHolders.get(i);
final String deviceName = device.getName();
if (deviceName != null && deviceName.length() > 0)
viewHolder.deviceName.setText(deviceName);
else
viewHolder.deviceName.setText(R.string.unknown_device);
viewHolder.deviceAddress.setText(device.getAddress());
viewHolder.deviceAd.setText(deviceHolder.additionalData);
viewHolder.deviceRssi.setText("rssi: "+Integer.toString(deviceHolder.rssi));
return view;
}
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
String deviceName = device.getName();
StringBuffer b = new StringBuffer();
int byteCtr = 0;
for( int i = 0 ; i < scanRecord.length ; ++i ) {
if( byteCtr > 0 )
b.append( " ");
b.append( Integer.toHexString( ((int)scanRecord[i]) & 0xFF));
++byteCtr;
if( byteCtr == 8 ) {
Log.d( LOG_TAG, new String( b ));
byteCtr = 0;
b = new StringBuffer();
}
}
ArrayList<AdElement> ads = AdParser.parseAdData(scanRecord);
StringBuffer sb = new StringBuffer();
for( int i = 0 ; i < ads.size() ; ++i ) {
AdElement e = ads.get(i);
if( i > 0 )
sb.append(" ; ");
sb.append(e.toString());
}
String additionalData = new String( sb );
Log.d(LOG_TAG, "additionalData: "+additionalData);
DeviceHolder deviceHolder = new DeviceHolder(device,additionalData,rssi);
runOnUiThread(new DeviceAddTask( deviceHolder ) );
}
};
class DeviceAddTask implements Runnable {
DeviceHolder deviceHolder;
public DeviceAddTask( DeviceHolder deviceHolder ) {
this.deviceHolder = deviceHolder;
}
public void run() {
mLeDeviceListAdapter.addDevice(deviceHolder);
mLeDeviceListAdapter.notifyDataSetChanged();
}
}
}
The xml layout is:
listitem_device.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f44336"
>
<TextView android:id="#+id/device_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24dp"/>
<TextView android:id="#+id/device_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12dp"/>
<TextView android:id="#+id/device_rssi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12dp"/>
<TextView android:id="#+id/device_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12dp"/>
actionbar_indeterminate_progress.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="56dp"
android:minWidth="56dp"
android:background="#f48fb1">
<ProgressBar android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"/>
I am developing an android app capable of detecting BLE signals and list them in a ListView. After an Scan period of time it will stop scanning. Here is the code (it is the same as in the developers page):
ScanBleActivity.class (extends from ScanBaseActivty):
public class ScanBleActivity extends ScanBaseActivity {
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler = new Handler();
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 20000;
/* (non-Javadoc)
* #see com.zishao.bletest.ScanBaseActivity#initScanBluetooth()
*/
protected void initScanBluetooth() {
BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();
startScanLen(true);
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mScanning) {
startScanLen(false);
}
}
/**
*
* #param enable
*/
private void startScanLen(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
addDevice(device, rssi);
}
};
AddDevice is implemented in other class, in this case ScanBaseActivity:
ScanBaseActivity (which extends from ListActivity):
abstract public class ScanBaseActivity extends ListActivity {
protected LeDeviceListAdapter mLeDeviceListAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_devices_scan);
mLeDeviceListAdapter = new LeDeviceListAdapter(this, new ArrayList<BluetoothDevice>());
this.setListAdapter(mLeDeviceListAdapter);
initScanBluetooth();
}
/**
* Start Scan Bluetooth
*
*/
abstract protected void initScanBluetooth();
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
BluetoothDevice device = (BluetoothDevice) mLeDeviceListAdapter.getItem(position);
ParcelUuid[] uuids = device.getUuids();
String uuidString = "Getting UUID's from " + device.getName() + ";UUID:";
if (null != uuids && uuids.length > 0) {
uuidString += uuids[0].getUuid().toString();
} else {
uuidString += "empty";
}
Toast.makeText(this, uuidString, Toast.LENGTH_LONG).show();
}
/**
* #param device
*/
protected synchronized void addDevice(final BluetoothDevice device, final int rssi) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mLeDeviceListAdapter.addDevice(device, rssi);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
}
All the information I detect (name, address, rssi, etc) is listed in a Listview. For the ListView I have implemented an Adapter called BaseAdapter. Part of the code of the Adapter is the following:
public class LeDeviceListAdapter extends BaseAdapter {
private List<BluetoothDevice> data;
private Activity context;
private final HashMap<BluetoothDevice, Integer> rssiMap = new HashMap<BluetoothDevice, Integer>();
public LeDeviceListAdapter(Activity context, List<BluetoothDevice> data) {
this.data = data;
this.context = context;
}
public synchronized void addDevice(BluetoothDevice device, int rssi) {
if(!data.contains(device) ){
data.add(device);
}
rssiMap.put(device, rssi);
}
#Override
public int getCount() {
return data.size();
}
#Override
public Object getItem(int position) {
return data.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
LayoutInflater mInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.leaf_devices_list_item, null);
convertView.setTag(new DeviceView(convertView));
}
DeviceView view = (DeviceView) convertView.getTag();
view.init((BluetoothDevice) getItem(position), null);
return convertView;
}
public class DeviceView {
private TextView title;
private TextView status;
private TextView type;
private TextView address;
private TextView rssivalue;
public DeviceView(View view) {
title = (TextView) view.findViewById(R.id.device_name);
status = (TextView) view.findViewById(R.id.device_status_txt);
type = (TextView) view.findViewById(R.id.device_type_txt);
address = (TextView) view.findViewById(R.id.device_address_txt);
rssivalue = (TextView) view.findViewById(id.signal_intensity_txt);
}
When the scan finishes I would like to save the ListView into a file (if it is possible into a xml file) but I donĀ“t know where and what code I should use in the project to save it. It would be imortant also to use a timestamp in the file to know when it was saved. Can anyone help me or give me any clues??
New: Code Edited to show All code from ScanBaseActivity and ScanBleActivity!!!
I see one problem with your constructor.
public LeDeviceListAdapter(Activity context, List<BluetoothDevice> data) {
//EDIT::
//add the following line
super(context, R.layout.leaf_devices_list_item, data);
this.data = data;
this.context = context;
}
Calling the super constructor passes the reference of data to the BaseAdapter. Then, BaseAdapter will be able to handle any changes in the list.
I am going to make a couple of assumptions here, so correct me if I am wrong.
You have access to your Adapter from the class that is scanning for the devices. I assume this is how you are adding devices to the adapter using add device. You could make a call from here to the adapter when scanning completes to get the List of devices you have stored, by creating a public method on the Adapter which returns a BluetoothDevice List.
The second option you have is to just create the xml file you want in the adapter whenever you add a device to the list. There should not be many devices around, so you should have a fairly small number of read writes. Writing to an xml file should follow the procedure in this answer.
Android creating and writing xml to file
(edit)
You would be looking at something like:
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
//Add the call here to save the file
// You should be able to access the list adapter from your base class
// that will give you the devices
}
}, SCAN_PERIOD);
I have a problem with a menu button called "Refresh" that lags for a long time when pressed. Basically, I'm running an async task that downloads xml from a web page and processes it and then finally updates the current list view. Is there some way to prevent this lag or refactor my code to make it more efficient?
Here is my activity where the refresh problem happens:
public class RouteView extends ListActivity implements ActivityUpdater {
private static ArrayList<String> mRouteNames;
private final String TAG = getClass().getName();
private TextView mTv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.route_view);
ListView lv = getListView();
LayoutInflater inflater = getLayoutInflater();
ViewGroup header = (ViewGroup) inflater.inflate(R.layout.header, lv, false);
lv.addHeaderView(header, null, false);
mTv = (TextView) findViewById(R.id.refresh_status);
long start = System.nanoTime();
fetchFeed();
Log.i("MagicBusV2", "Time: " + (System.nanoTime() - start));
}
#Override
protected void onResume() {
super.onResume();
fetchFeed();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0, Constants.REFRESH_MENU_ID, 0, R.string.refresh).setIcon(R.drawable.refresh);
return result;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean result = super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case Constants.REFRESH_MENU_ID:
long start = System.nanoTime();
fetchFeed();
Log.i("MagicBusV2", "Refresh time: " + (System.nanoTime() - start));
break;
}
return result;
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Log.i(TAG, "id: " + (int) id);
Log.i(TAG, "num_routes: " + mRouteNames.size());
String routeName = mRouteNames.get((int) id);
Intent intent = new Intent(this, StopView.class);
intent.putExtra(Constants.ROUTE_NAME, routeName);
intent.putExtra(Constants.STOP_VIEW_TYPE, Constants.STOPS_ROUTE);
startActivity(intent);
}
public void refreshUI() {
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mRouteNames));
getListView().setTextFilterEnabled(true); // allows users to type to filter results, need to inform them of this some way
mTv.setText("Refreshed!");
}
public static void setRouteNames(ArrayList<String> route_names) {
mRouteNames = route_names;
}
private void fetchFeed() {
mTv.setText("Refreshing...");
new RefreshFeedTask(this).execute("null");
}
And here is my AsyncTask code:
public class RefreshFeedTask extends AsyncTask<String, Integer, Void> {
private ArrayList<Route> routes;
private TimeFeed feed;
private ActivityUpdater updater;
private Context mCtx;
public RefreshFeedTask(Context ctx) {
mCtx = ctx;
updater = (ActivityUpdater) mCtx;
routes = new ArrayList<Route>();
try {
feed = new TimeFeed();
} catch (MBusDataException e) {
e.printStackTrace();
}
}
protected Void doInBackground(String... route_name) {
routes = feed.getRoutes();
int num_routes = routes.size();
if(route_name[0].equals("null")) {
ArrayList<String> route_names_list = new ArrayList<String>(num_routes);
for (int i = 0; i < num_routes; i++) {
route_names_list.add(routes.get(i).getName());
}
RouteView.setRouteNames(route_names_list);
} else if(route_name[0].equals("all_stops")) {
ArrayList<Stop> stops = new ArrayList<Stop>();
for (Route route : routes) {
for (Stop stop : route.getStops()) {
if(!stops.contains(stop)) {
stops.add(stop);
}
}
}
StopView.setStops(stops);
} else if(route_name.length > 1) {
Route route = feed.getRouteWithName(route_name[0]);
StopDetailsView.setStop(route.getStopWithName(route_name[1]));
} else if(route_name[0].equals("favorites")) {
ArrayList<Stop> fav_stops = new ArrayList<Stop>();
ArrayList<Favorite> allFavs = FavoritesStore.getInstance(mCtx).getAllFavorites();
for (Favorite fav : allFavs) {
Route route = feed.getRouteWithName(fav.getRouteName());
if (route != null) {
Stop stop = route.getStopWithName(fav.getStopName());
if (stop != null) {
fav_stops.add(stop);
}
}
}
StopView.setStops(fav_stops);
} else {
StopView.setStops(feed.getRouteWithName(route_name[0]).getStops());
}
return null;
}
protected void onPostExecute(Void ignore) {
updater.refreshUI();
}
Thanks for any help you can give. I apologize if I posted too much code; I wasn't sure how much was needed to find potential problems.
Look at the constructor of the TimeFeed class. Since you said, that you are downloading and parsing stuff, and I believe that you are perhaps doing those things in the constructor, it will obviously take some time to execute that statement.