Search code examples
androidkotlinbroadcastreceivercurrentlocationfusedlocationproviderclient

How to get device location when a BTLE event triggers in broadcast receiver?


I am statically registering Broadcast Receiver for BTLE connection/disconnection events to detect when my bluetooth headphones connects/disconnects with my Android phone. I also want to record the current location of my phone to update to a server when the OnReceive event for bluetooth is triggered. I am using FusedLocationProviderClient to get current location with fine, coarse and background location permissions declared in the manifest, but the location received is always null. How do I get the correct current location? Here's my code:

AndroidManifest.xml - permissions and static receiver

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>


        <receiver android:name=".BtBroadcastReceiver" android:exported="true">
            <intent-filter>
                <action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
                <action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
                <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
            </intent-filter>
        </receiver>

BtBroadcastReceiver.kt

class BtBroadcastReceiver : BroadcastReceiver() {
    private var locationCallback: LocationCallback? = null
    private var locationRequest: LocationRequest? = null
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private var currentLocation: Location? = null


    override fun onReceive(context: Context, intent: Intent) {        
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)

        val action = intent.action
        val bluetoothDevice: BluetoothDevice

        when (action) {
            BluetoothDevice.ACTION_ACL_CONNECTED -> {
                bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)!!
                updateDeviceLocation(context, bluetoothDevice.name)
            }
            BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
                bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)!!
                updateDeviceLocation(context, bluetoothDevice.name)
            }
        }
    }

    private fun updateDeviceLocation(context: Context, name: String) {
        fusedLocationClient.lastLocation
            .addOnSuccessListener { location->
                if (location != null) {
                    currentLocation = location
                    // Save location of device to Shared Preferences
                }

            }    
        }

Solution

  • fusedLocationClient.lastLocation can return null location if no recent location fix exists. Using the below code to get current location works as it gets fresh location:

    fusedLocationClient.getCurrentLocation(LocationRequest.PRIORITY_HIGH_ACCURACY, object : CancellationToken() {
                override fun onCanceledRequested(p0: OnTokenCanceledListener) = CancellationTokenSource().token
    
                override fun isCancellationRequested() = false
            })
                .addOnSuccessListener { location: Location? ->
                    //Update location in shared preferences
    
                }
    

    Make sure to use the latest location package in gradle based on this post

    implementation "com.google.android.gms:play-services-location:17.1.0"