Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nearby Messages using an IntentService

Initially I setup a BroadcastReceiver to receive intents from the Nearby Messages API.

class BeaconMessageReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Nearby.getMessagesClient(context).handleIntent(intent, object : MessageListener() {
            override fun onFound(message: Message) {
                val id = IBeaconId.from(message)
                Timber.i("Found iBeacon=$id")
                sendNotification(context, "Found iBeacon=$id")
            }

            override fun onLost(message: Message) {
                val id = IBeaconId.from(message)
                Timber.i("Lost iBeacon=$id")
                sendNotification(context, "Lost iBeacon=$id")
            }
        })
    }

    private fun sendNotification(context: Context, text: String) {
        Timber.d("Send notification.")
        val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        val notification = NotificationCompat.Builder(context, Notifications.CHANNEL_GENERAL)
                .setContentTitle("Beacons")
                .setContentText(text)
                .setSmallIcon(R.drawable.ic_notification_white)
                .build()

        manager.notify(NotificationIdGenerator.nextID(), notification)
    }

}

Then registered this receiver in my MainActivity after location permissions have been granted.

class MainActivity : AppCompatActivity() {

    // ...

    private fun onLocationPermissionsGranted() {
        val filter = MessageFilter.Builder()
                .includeIBeaconIds(UUID.fromString("B9407F30-F5F8-466E-AFF9-25556B57FEED"), null, null)
                .build()

        val options = SubscribeOptions.Builder().setStrategy(Strategy.BLE_ONLY).setFilter(filter).build()

        Nearby.getMessagesClient(context).subscribe(getPendingIntent(), options)
    }

    private fun getPendingIntent(): PendingIntent = PendingIntent.getBroadcast(
            this, 0, Intent(context, BeaconMessageReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)

}

This worked well while the app was open, but does not work when the app is closed. So I found this example, that demonstrates how to setup an IntentService to receive messages while the app is in the background.

The example does use the Nearby.Messages class, which was deprecated in favor of the MessagesClient. So I replaced the deprecated code with the MessagesClient implementation.

class MainActivity : AppCompatActivity() {

    // ...

    private fun onLocationPermissionsGranted() {
        val filter = MessageFilter.Builder()
                .includeIBeaconIds(UUID.fromString("B9407F30-F5F8-466E-AFF9-25556B57FEED"), null, null)
                .build()

        val options = SubscribeOptions.Builder().setStrategy(Strategy.BLE_ONLY).setFilter(filter).build()

        Nearby.getMessagesClient(context).subscribe(getPendingIntent(), options)
            .addOnSuccessListener {
                Timber.i("Subscribed successfully.")
                startService(Intent(this, BeaconMessageIntentService::class.java))
            }.addOnFailureListener {
                Timber.e(exception, "Subscription failed.")
            }
    }

    private fun getPendingIntent(): PendingIntent = PendingIntent.getBroadcast(
            this, 0, Intent(context, BeaconMessageIntentService::class.java), PendingIntent.FLAG_UPDATE_CURRENT)

}

And this is the IntentService (which is almost identical to my BroadcastReceiver).

class BeaconMessageIntentService : IntentService("BeaconMessageIntentService") {

    override fun onHandleIntent(intent: Intent?) {
        intent?.let {
            Nearby.getMessagesClient(this)
                    .handleIntent(it, object : MessageListener() {
                        override fun onFound(message: Message) {
                            val id = IBeaconId.from(message)
                            Timber.i("Found iBeacon=$id")
                            sendNotification("Found iBeacon=$id")
                        }

                        override fun onLost(message: Message) {
                            val id = IBeaconId.from(message)
                            Timber.i("Lost iBeacon=$id")
                            sendNotification("Lost iBeacon=$id")
                        }
                    })
        }
    }

    private fun sendNotification(text: String) {
        Timber.d("Send notification.")
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        val notification = NotificationCompat.Builder(this, Notifications.CHANNEL_GENERAL)
                .setContentTitle("Beacons")
                .setContentText(text)
                .setSmallIcon(R.drawable.ic_notification_white)
                .build()

        manager.notify(NotificationIdGenerator.nextID(), notification)
    }

}

onHandleIntent is called, and the Intent is not null; yet for some reason onFound() and onLost() are never called. Why would this be the case?

like image 654
Bryan Avatar asked Dec 05 '18 16:12

Bryan


People also ask

What is an intentservice in Android?

The IntentService is a subclass of the Service class that provides an Android specific implementation of this pattern. It will manage queueing work, starting up a worker thread to service the queue, and pulling requests off the queue to be run on the worker thread.

When should I use an intent service?

Step 1: Defining the Processing of Messages. One good reason to use an IntentService is when you have work that needs to occur off the main thread to keep the application responsive and efficient. Another reason is when you may have multiple processing requests, and they need to be queued up and handled on the fly.

What is the Nearby Messages API?

The Nearby Messages API combines internet connectivity, Bluetooth, and other technologies to deliver this feature. A Google Account (i.e. Gmail address) for obtaining a Google API Key Two Android devices with Google Play Services (in other words, the Play Store) installed on them

How do I send a work request to an intentservice?

Work is sent to an IntentService by instantiating an Intent and then calling the StartService method with that Intent as a parameter. The Intent will be passed to the service as a parameter in the OnHandleIntent method. This code snippet is an example of sending a work request to an Intent:


1 Answers

It's not really a solution but what I found is this (credit to this answer):
I've tried a few configurations including a BroadcastReceiver and adding a JobIntentService to run the code in the background, but every time I got this the onExpired callback which you can set to the SubscribeOptions:

options.setCallback(new SubscribeCallback() {
    @Override
    public void onExpired() {
        super.onExpired();
        Toast.makeText(context.get(), "No longer Subscribing!", Toast.LENGTH_SHORT).show();
    }
}

When the subscribe occurred in the background it was delayed, but it was still called.

Notes:
1. When I've tested with Strategy.BLE_ONLY I did not get the onFound callback.
2. From Google's documentation:

Background subscriptions consumes less power than foreground subscriptions, but have higher latency and lower reliability

When testing I found this "lower reliability" to be an understatement: onFound was rarely called and I never got the onLost.

like image 154
MikeL Avatar answered Oct 28 '22 20:10

MikeL