Search code examples
androidbluetoothandroid-broadcastandroid-bluetooth

Android Broadcast Receiver bluetooth events catching


I'm trying to catch bluetooth state changes with Broadcast Receiver.

My manifest:

<uses-permission android:name="android.permission.BLUETOOTH" />
<application>
     <activity
        android:name=".MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <receiver android:name=".BluetoothBroadcastReceiver"
              android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
            <action android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
            <action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
            <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
        </intent-filter>
    </receiver>
</application>

Receiver onReceive method:

public void onReceive(Context context, Intent intent) {

    String action = intent.getAction();
    Log.d("BroadcastActions", "Action "+action+"received");
    int state;
    BluetoothDevice bluetoothDevice;

    switch(action)
    {
        case BluetoothAdapter.ACTION_STATE_CHANGED:
            state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
            if (state == BluetoothAdapter.STATE_OFF)
            {
                Toast.makeText(context, "Bluetooth is off", Toast.LENGTH_SHORT).show();
                Log.d("BroadcastActions", "Bluetooth is off");
            }
            else if (state == BluetoothAdapter.STATE_TURNING_OFF)
            {
                Toast.makeText(context, "Bluetooth is turning off", Toast.LENGTH_SHORT).show();
                Log.d("BroadcastActions", "Bluetooth is turning off");
            }
            else if(state == BluetoothAdapter.STATE_ON)
            {
                Log.d("BroadcastActions", "Bluetooth is on");
            }
            break;

        case BluetoothDevice.ACTION_ACL_CONNECTED:
            bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            Toast.makeText(context, "Connected to "+bluetoothDevice.getName(),
                    Toast.LENGTH_SHORT).show();
            Log.d("BroadcastActions", "Connected to "+bluetoothDevice.getName());
            break;

        case BluetoothDevice.ACTION_ACL_DISCONNECTED:
            bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            Toast.makeText(context, "Disconnected from "+bluetoothDevice.getName(),
                    Toast.LENGTH_SHORT).show();
            break;
    }
}

I launch app then minimize it by pressing Home button. Go to settings and turn on bluetooth but nothing happens. Though I expect toast and logcat messages. What's wrong here?


Solution

  • In order to catch Bluetooth state changes (STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF), do this:

    First, add Bluetooth permission to your AndroidManifest file:

    <uses-permission android:name="android.permission.BLUETOOTH" />
    

    Create a BroadcastReceiver in your Activity or Service:

        private final BroadcastReceiver mBroadcastReceiver1 = new BroadcastReceiver() {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
    
            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                switch(state) {
                    case BluetoothAdapter.STATE_OFF:
                        ..
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        ..
                        break;
                    case BluetoothAdapter.STATE_ON:
                        ..
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        ..
                        break;
                }
    
            }
        }
    };
    

    Create an IntentFilter and register it with BroadcastReceiver in your Activity/Service's onCreate() method:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        IntentFilter filter1 = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        registerReceiver(mBroadcastReceiver1, filter1);
        
        ...
    }
    

    Unregister BroadcastReceiver in your onDestroy() method:

    @Override
    protected void onDestroy() {
        super.onDestroy();
    
        unregisterReceiver(mBroadcastReceiver1);
    }
    

    In order to catch changes of discoverability of device (SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE), create another BroadcastReceiver and register/unregister to your Activity as I mentioned above. Only difference between those BroadcastReceivers is the first one uses BluetoothAdapter.EXTRA_STATE and the other one uses BluetoothAdapter.EXTRA_SCAN_MODE. Here is the example code for BroadcastReceiver to catch discoverability changes:

    Create an IntentFilter and register it in onCreate() method:

    IntentFilter filter2 = new IntentFilter();
    filter2.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
    filter2.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    filter2.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
    registerReceiver(mBroadcastReceiver2, filter2);
    

    Create the BroadcastReciver in Activity/Service to catch discoverability changes:

        private final BroadcastReceiver mBroadcastReceiver2 = new BroadcastReceiver() {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
    
            if(action.equals(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)) {
    
                int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.ERROR);
    
                switch(mode){
                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
                        ..
                        break;
                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
                        ..
                        break;
                    case BluetoothAdapter.SCAN_MODE_NONE:
                        ..
                        break;
                }
            }
        }
    };
    

    And lastly unregister BroadcastReciver in onDestroy():

    unregisterReceiver(mBroadcastReceiver2);
    

    Note that, you don't need to add any <intent-filter> or <receiver> to your AndroidManifest file, except you need to add Bluetooth permission of course.

    If you want to catch (ACTION_ACL_CONNECTED, ACTION_ACL_DISCONNECTED, ACTION_ACL_DISCONNECT_REQUESTED), now you need to add an <intent-filter> to your AndroidManifest file:

    <intent-filter>
        <action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
        <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
    </intent-filter>
    

    Create filter and register it in onCreate() method:

    IntentFilter filter3 = new IntentFilter();
    filter3.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
    filter3.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    registerReceiver(mBroadcastReceiver3, filter3);
    

    Then create the BroadcastReceiver in your Activity/Service:

        private final BroadcastReceiver mBroadcastReceiver3 = new BroadcastReceiver() {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
    
            switch (action){
                case BluetoothDevice.ACTION_ACL_CONNECTED:
                    ..
                    break;
                case BluetoothDevice.ACTION_ACL_DISCONNECTED:
                    ..
                    break;
            }
        }
    };
    

    And lastly, unregister:

    unregisterReceiver(mBroadcastReceiver3);
    

    If you want to read more about state constants, this is from the documentation:

    public static final String EXTRA_STATE:

    Used as an int extra field in ACTION_STATE_CHANGED intents to request the current power state. Possible values are: STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF

    public static final String EXTRA_SCAN_MODE:

    Used as an int extra field in ACTION_SCAN_MODE_CHANGED intents to request the current scan mode. Possible values are: SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE