Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optionally starting activities and using notifications from services in Android. Only launch or notify if a certain app is present

I have a service I am reusing (it a both a "bound" and "started" service) in my own app because it does a lot of useful data acquisition I'm interested in. Everything was working but I noticed a problem. An exception is thrown in this code:

Intent dialogIntent = new Intent();
    dialogIntent.setClassName(service.getBaseContext(), "com.mycompany.receiver.ui.DialogActivity"); // names changed to protect the innocent
    dialogIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
    dialogIntent.putExtra("message", message);
    service.getApplicationContext().startActivity(dialogIntent);

The exception warns about "unknown activity" and "have you checked the manifest". Now, this is not caused by me not putting the activity in the manifest. In fact, there really should be an exception thrown here because my application doesn't have this activity! This was an activity written for the original application. I didn't notice it at first because this code is only hit when the data gathered is outside a certain range. I can change the service code, but the service and original application have to continue to work as before. It's just that in my application, I'm not particularly interested in popping up an activity for this particular message (it's not that important to me).
I did think about this, and I could just throw a try/catch block around everything. But that seems a little hacky. If something else is going wrong in the original app/service pair, I want to know about it. I came up with the following ideas, which I need a helping hand to point me in the right direction.

  1. Is there a way for the service to know what applications are currently running?
  2. Or perhaps currently in the foreground?
  3. Or how about currently bound to the service?

I'm interested in 1-3 (for general knowledge and for solving this problem) but 2 is probably the most useful. After all, there could be multiple apps bound to the service, and I'm not sure I want to bring the original app to the foreground over my own just because of this message. And finally.... Forget everything I said, and 4. How would you solve this?

I have the same a very similar problem elsewhere in the code:

Intent toLaunch = new Intent();
    toLaunch.setClassName(context, "com.mycompany.receiver.ui.BankingActivity");

    toLaunch.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent intentBack = PendingIntent.getActivity(context, 0, toLaunch, PendingIntent.FLAG_CANCEL_CURRENT);

    notify.setLatestEventInfo(context, title, message, intentBack);
    notifier.notify(NOTIFY_1, notify);

This one doesn't throw an exception; it puts up a notification and when I click on it, the original app is launched. This might actually be ok, but again, it might be nice not to have this (or perhaps a compromise - have the notifier notify if the app is at least bound, but not necessarily in foreground). Thoughts?

Many thanks, Dave

like image 640
Dave Avatar asked Jan 06 '12 17:01

Dave


1 Answers

How would you solve this?

You never really stated what "this" is. I am going to guess that "this" is:

How do I have a service notify a foreground activity of mine if there is one, and otherwise raise a Notification (or perhaps do nothing at all)?

For that "this", use an ordered broadcast. As I wrote in a blog post from a ways back, here is the recipe for either notifying a foreground activity or raising a Notification:

  1. Define an action string you will use when the event occurs that you want to go to the activity or notification (e.g., com.commonsware.java.packages.are.fun.EVENT).

  2. Dynamically register a BroadcastReceiever in your activity, with an IntentFilter set up for the aforementioned action string and with a positive priority (the default priority for a filter is 0). This receiver should then have the activity do whatever it needs to do to update the UI based on this event. The receiver should also call abortBroadcast() to prevent others from getting it. Be sure to register the receiver in onStart() or onResume() and unregister the receiver in the corresponding onStop() or onPause() method.

  3. Register in your manifest a BroadcastReceiver, with an <intent-filter> set up for the aforementioned action string. This receiver should raise the Notification.

  4. In your service (e.g., an IntentService), when the event occurs, call sendOrderedBroadcast().

If you simply want to notify one of your foreground activities, but do nothing if it is not in the foreground, skip step #3.

Here is a sample project that demonstrates this.

Is there a way for the service to know what applications are currently running?

You can ask ActivityManager, but, frankly, that's asking for a fragile app.

Or perhaps currently in the foreground?

Not reliably.

Or how about currently bound to the service?

Only if you track it yourself.

like image 107
CommonsWare Avatar answered Oct 10 '22 00:10

CommonsWare