0

I'm trying to program an App to establish a basic communication over BLE between an Android Phone and an RFDuino. The RFDuino is just advertising and can be detected by the "BLE Scanner" App. Below is the draft code for the activity, that is supposed to detect BLE devices.

The problem is, that no devices are detected. The Debug Log shows, that the method "onLeScan" is not even called once...

I'm running out of Ideas and would be thankful for every hint :-)

package de.tuhh.et5.serialcommunicator;

import (...)

public class BLEActivity extends AppCompatActivity {
    private static final long SCAN_PERIOD = 10000;
    private int REQUEST_ENABLE_BT = 1;
    public final static String TAG = "BLE_Activity"; // Tag for the LOG

    private BluetoothAdapter ble_adapter;
    private boolean scanning;
    private Handler ble_handler;
    private BLE_DeviceListAdapter BLE_DeviceListAdapter;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        d("on Create");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ble);          

        TabHost tabHost=(TabHost)findViewById(R.id.tabHost);
        tabHost.setup();

        TabHost.TabSpec t1=tabHost.newTabSpec("Tab1");
        t1.setContent(R.id.tab1_layout);
        t1.setIndicator("Devices");
        tabHost.addTab(t1);

        BLE_DeviceListAdapter = new BLE_DeviceListAdapter();

       ListView ble_list = (ListView)findViewById(R.id.BLE_DeviceListView);
        ble_list.setAdapter(BLE_DeviceListAdapter);

        ble_handler = new Handler();
        // get a bluetooth adapter from the bluetooth manager
        final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        ble_adapter = bluetoothManager.getAdapter();

        // Check if BLE is supported by the Android device
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "BLE Not Supported",
                    Toast.LENGTH_SHORT).show();
            finish();
        }

        // Enable bluetooth
        if (ble_adapter == null || !ble_adapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

    }


    // menu stuff
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        d("menu inflates");
        // Inflate the menu; this adds items to the action bar if it is present.
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.ble_menu, menu);

        if (scanning) {
            menu.findItem(R.id.scanning_start).setVisible(false);
        } else {
            menu.findItem(R.id.scanning_start).setVisible(true);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.scanning_start:
                d("scan pressed");
                BLE_DeviceListAdapter.clear();
                ble_scan(true);
                break;
        }
        return true;
    }

    private void ble_scan(final boolean en){
        d("Scan start");
        // stopLeScan and startLeScan are deprecated since Android 5.0, but still work.
        // to offer a compatibility down to Android 4.3 the old methods are used
        if (en){
            ble_handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    d("scan timer finished");
                    scanning = false;
                    ble_adapter.stopLeScan(ble_callback);
                    invalidateOptionsMenu();
                }
            },SCAN_PERIOD);
            scanning = true;
            ble_adapter.startLeScan(ble_callback);
        }else{
            scanning = false;
            ble_adapter.stopLeScan(ble_callback);
        }
        invalidateOptionsMenu();
    }

    private BluetoothAdapter.LeScanCallback ble_callback = new BluetoothAdapter.LeScanCallback(){

        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord){
            d("onLeScan");
            runOnUiThread(new Runnable(){
                @Override
                public void run(){

                    BLE_DeviceListAdapter.addDevice(device);
                    BLE_DeviceListAdapter.notifyDataSetChanged();

                }
            });
        }
    };


    // This class lists the elements in an ble device entry as defined in "ble_device_entry.cml"
    // It is needed in the BLE_DeviceListAdapter class
    private class ViewHolder {
        TextView name;
        TextView device_id;
        TextView device_adress;
    }

    // #####################################################
    // private Class BLE_DeviceListAdapter
    //
    // Adapter to handle found BLE Devices
    // #####################################################

    private class BLE_DeviceListAdapter extends BaseAdapter{

        private ArrayList<BluetoothDevice> ble_devices; // holds found devices
        private LayoutInflater ble_inflater;            // layout inflater

        public BLE_DeviceListAdapter(){
            super();
            // Init
            ble_devices = new ArrayList<>();
            ble_inflater = BLEActivity.this.getLayoutInflater();
        }

        // method to add a device
        public void addDevice(BluetoothDevice ble_device){
            // device is added, if it is not already in the list
            if (!ble_devices.contains(ble_device)){
                ble_devices.add(ble_device);
            }
        }

        // Mandatory, returns device at position ...
        @Override
        public BluetoothDevice getItem(int position){
            return ble_devices.get(position);
        }

        // clear list..
        public void clear(){
            ble_devices.clear();
        }

        // Mandatory: Not needed, therefore just a dummy
        @Override
        public long getItemId(int i) {
            return i;
        }

        // Mandatory: returns size of list
        @Override
        public int getCount() {
            return ble_devices.size();
        }

        // Mandatory: creates and returns view with a device entry
        @Override
        public View getView(int i, View view, ViewGroup viewGroup ){

            ViewHolder viewHolder; // create view holder

            // if the view does not already exist:
            if (view == null){

                view = ble_inflater.inflate(R.layout.ble_device_entry, null);// inflate view
                viewHolder = new ViewHolder(); // create a ViewHolder
                // Connenct the ViewHolder elements to layout elements
                viewHolder.name = (TextView)findViewById(R.id.BLE_sName);
                viewHolder.device_adress = (TextView)findViewById(R.id.BLE_sAdress);
                viewHolder.device_id = (TextView)findViewById(R.id.BLE_sID);
                view.setTag(viewHolder); // Add the ViewHolder as a Tag to the view
            }else{
                // if the view already exists
                viewHolder = (ViewHolder) view.getTag(); // get Tag from view to populate ViewHolder
            }
            // get BLE Device
            Log.d("test", "hier so....");
            BluetoothDevice device = ble_devices.get(i);
            // get device name
            final String deviceName = device.getName();
            // assign device name to ViewHolder Object
            if (deviceName != null && deviceName.length() > 0){
                viewHolder.device_id.setText(deviceName);
            }else viewHolder.device_id.setText(R.string.unknown_ble_device);
            // assign more values
            viewHolder.device_adress.setText(device.getAddress());
            viewHolder.name.setText(R.string.unknown_ble_device);
            d("before return view");
            return view;
        }

    }
    // #####################################################
    // End Adapter
    // #####################################################

    public void d(Object msg) {
        Log.d(TAG, ">==< " + msg.toString() + " >==<");
    }

    public void e(Object msg) {
        Log.e(TAG, ">==< " + msg.toString() + " >==<");
    }

}
Chuchaki
  • 361
  • 1
  • 3
  • 14

2 Answers2

1

Ok, it seems i just messed up the permissions. I did not have the runtime permission for the location. I thought the manifest based permission are enough if one does not exclusevliy target Android 6.xx . Now I have problems with the view holder and stuff, but thats another Story :-).

Anyways thanks for the Solution @MarkusKauppinen!

Chuchaki
  • 361
  • 1
  • 3
  • 14
0

The scanning code looks okay in my opinion.

Some things to check:

Did you debug/log to check that options menu works and ble_scan() gets called?

Did you debug/log to check that startLeScan() returns true?

The onLeScan() callback is not called if no devices were found.

Since you are scanning for 10 seconds, the RFDuino should advertise itself often enough/for long enough that at least one advertising cycle always occurs within the 10 second window.

All Android versions will need these permissions:

android.permission.BLUETOOTH

android.permission.BLUETOOTH_ADMIN

Additionally Android 6.x (Marhsmallow) and later also need:

android.permission.ACCESS_COARSE_LOCATION

And if you are writing code targeting just 6.x and higher you need to use the new runtime permission model instead of the old manifest file based.

On some devices running Android 6.x the system wide "Location" setting must be ON. See: onLeScan from BluetoothAdapter.LeScanCallback not called in Android Marshmallow

And then of course some of the older discussions may have some useful hints.

Community
  • 1
  • 1
Markus Kauppinen
  • 3,025
  • 4
  • 20
  • 30
  • Hey! Thanks for the input! I'll look into it first thing tomorrow! – Chuchaki Apr 15 '16 at 17:41
  • Hey! ble_scan is called, startLescan returns true, and all the permissions are set. onLeScan is still not called, and no device is listed. The RFDuino is advertising in intervalls of 500ms, and i can detect the RFDuino with other apps. I already (at least I think so :-) ) considered all threads dealing with this topic. Location service is on, and since I want a compabilitiy to Android 4.3, i use the manifest. – Chuchaki Apr 16 '16 at 14:08