I have an Android library that has a Service that creates a Notification. From what I understand Notification must have a contentIntent (PendingIntent) set or a runtime exception will be thrown.
The problem is that I want users to be able to use this Service as is, or extend it, so that they can set the PendingIntent themselves through a callback during the creation of the Notification. However, if they choose not to do this, I need to set the PendingIntent to something so that there is no exception. Is there any way to create a dummy PendingIntent that just acts as a fill-in?
Here's an example of the code from the createNotification method:
PendingIntent p;
if(getPendingIntentCallback != null) {
p = getPendingIntentCallback.getPendingIntent();
}
else {
p = ?;
}
notification.contentIntent = p;
PendingIntent pi=PendingIntent.getBroadcast(this, 0, new Intent("DoNothing"), 0);
This worked for me. It will launch a broacast for a "DoNothing" action. I hope nobody will listen for a "DoNothing" broadcast and do something as a reaction to it. But if you prefer you can construct something more exotic.
Note: I'm using this just as a temporary placeholder. I will substitute it with something more useful for the user as I will advance in my application development.
A notification needs to have some sort of action associated with it. You have to tell android what to do when a app user clicks the notification. Letting your library fail if no contentIntent has been defined will let your library user know that they have missed a very important step.
You could check for the pending intent before creating your notification.
if(getPendingIntentCallback != null) {
p = getPendingIntentCallback.getPendingIntent();
// create a notification
}
else {
//don't create a notification
Log.d("Notification", "Your notification was not created because no pending intent was found");
}
Or to answer the question you asked, you could create a dummy pending intent that performs some arbitrary action like going to the home screen.
See this thread:
How to Launch Home Screen Programmatically in Android
Related
I have a question regarding proximity alerts.
In all tutorials I ve read they are created and destroyed while the activity that create them is still running.
But what happens if say an activity creates n proximity alerts and then the activity itself is destroyed (the PA are not)
Then if I want to build another activity that finds these Proximity Alerts, how can I do that? Is that even possible?
You have to maintain your own list of proximity alerts. There is no way to get them back. However, #Mercato is correct when he says that you can remove a PA using only pending intents, but you don't have to store them. According to the docs:
A PendingIntent itself is simply a reference to a token maintained by the system describing the original data used to retrieve it. This means that, even if its owning application's process is killed, the PendingIntent itself will remain usable from other processes that have been given it. If the creating application later re-retrieves the same kind of PendingIntent (same operation, same Intent action, data, categories, and components, and same flags), it will receive a PendingIntent representing the same token if that is still valid, and can thus call cancel() to remove it.
This means that the system will store your PendingIntent for you between app restarts, and you can retrieve it by passing the same Intent you used to create it. So for example, if you created the following PendingIntent:
Intent intent = new Intent(context, Foo.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Then all you have to store is the requestId (1) and the Class or class name (Foo.class or Foo.class.getName()). Then if you want to retrieve that same PendingIntent without creating a new one, you can do the following:
Class<Foo> className = retrieveClass(); //You implement this
//String clazz = retrieveClassName(); //This is another option
int requestId = retrieveId(); //You implement this
Intent intent = new Intent(context, className);
//The flag given attempts to retrieve the PendingIntent if it exists, returns null if it doesn't.
PendingIntent pi = PendingIntent.getBroadcast(context, requestId, intent, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
//This pending intent was registered once before.
//Go ahead and call the function to remove the PA. Also, go ahead and call pi.cancel() on this.
}
else {
//This pending intent was not registered, and therefore can't have a PA registered to it.
}
Technically, all proximity alerts need a PendingIntent defined and used as a parameter. Android's Documentation shows that if you know the list of PendingIntents then you can remove them as well.
removeProximityAlert(PendingIntent intent) Removes the proximity alert
with the given PendingIntent.
Since PendingIntent is Parecelable see here then you could add it as an Extra to any Intent. This means, that on starting another Activity, you can create an Parcelable[] array to hold all these PendingIntent, then
putExtra(String name, Parcelable[] value)
Add extended data to the intent.
then retrieve them in the next Activity via getIntent() and it's relevant methods.
I am trying to create a notification where I add button to it that would basically do some action. I know I can do the following
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(con)
.setSmallIcon(image)
.setContentTitle("title")
.addAction(icon, title, intent)
My questions are:
1) Is adding button supported in API 5.0+ ONLY or also in 4.x? I read different answers about it
2) The action seems to be associated with opening an activity. Is there away I can have it so when you click on a button it takes an action without having to open the activity (Either through broadcast receiver or some other way)? As far as I know Intent opens activities.
Thank you so much
It will work in Android 4.1 and later. See official doc.
If you want to do action with out any UI update(i.e., showing any activity), I suggest send a pending intent(which will trigger a broadcast receiver) as a parameter for notification action
i) Create a BroadcastReceiver named MyBroadcastReceiver
ii) Add your action in BroadcastReceiver's onReceive method
iii) Create a PendingIntent
Intent mIntent = new Intent(this,MyBroadcastReceiver.class);
PendingIntent mPendingIntent = PendingIntent.getBroadcast(this, 0, mIntent , 0);
iv) Add it to Notification
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(con)
.setSmallIcon(image)
.setContentTitle("title")
.addAction(icon, title, mPendingIntent)
1) Is adding button supported in API 5.0+ ONLY or also in 4.x?
It will work in Android 4.1 and later. See official doc.
2)...As far as I know Intent opens activities.
Yes, you can use BroadcastReceiver or Service for executing logic that doesn't involve UI. First of all, you can build intent to launch activity, broadcast receiver, or service. Secondly, the third argument of NoticiationCompat.Builder#addAction is PendingIntent, not an Intent. You can use PendingIntent.getService to create an PendingIntent for service, for instance.
http://developer.android.com/reference/android/app/PendingIntent.html#getService(android.content.Context, int, android.content.Intent, int)
I wante to create some notifications to user.
Ex : when a new comment, when someone ask question,...
I know how to create notification, but i don't know how can i do for send a notification just one time.
Example : notification is send to user because he has a new comment. He click on the notification for start application and watch the comment. But now, how can i avoid that it receives twice the same notification ?
It is recommended to store it in a database, in "notification" table or it exist a system for manage this ?
I'm sorry for the very bad English.
Have you looked at Google Cloud Messaging?
https://developers.google.com/cloud-messaging/android/start
If you've already implemented this, and you're receiving the same notification twice, it might have something to do with how you are building the Notification. If the link above doesn't help, can you post more information, and possibly the code?
I solved the problem by checking if the notification is already displayed.
private boolean isNotificationVisible() {
Intent notificationIntent = new Intent(context, MainActivity.class);
PendingIntent test = PendingIntent.getActivity(context, MY_ID, notificationIntent, PendingIntent.FLAG_NO_CREATE);
return test != null;
I am attempting to raise a notification that a message has arrived. I have added an action expecting an icon (smallredball) to show on the notification. I expect that if the user hits smallredball, main activity will startup and the activity, checking the extras bundle, will see the orders to do something different than if it were just started up normally.
The notification shows on the target phone (running KitKat) along with the text but the smallredball icon never shows. When the user touches the notification the activity executes with no extra. EDIT: THE ACTIVITY IS NOW GETTING THE EXTRA BUNDLE.
This is the code sending the notification:
private void raiseNotification( String username, String mesText)
{
DebugLog.debugLog("GCMIntentService: Attempting to Raise Notification ", false);
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("whattodo", "showmessage");
intent.setAction(Long.toString(System.currentTimeMillis())); //just to make it unique from the next one
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
b.setContentTitle("New SafeTalk Message")
.setSmallIcon(R.drawable.note24x24)
.setContentText("From " + username + " " + mesText)
.setTicker("New SafeTalk Message")
.setContentIntent(pIntent)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setAutoCancel(true)
.addAction(R.drawable.smallredball, "Read Now", pIntent);
NotificationManager mgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
mgr.notify(0, b.build());
}
this is a code snippet from the activity:
Bundle extras = getIntent().getExtras();
if (extras == null)
{
GlobalStuff.mpBad.start();
}
else
{
String myOrders = extras.getString("whattodo");
if (myOrders.equals("showmessage"))
GlobalStuff.mpBeep.start();
}
Why isn't the icon showing in the notification? Since I setAutoCancel to true, I expected that simply touching the notification would make it just go away. But instead it runs the app providing no extra bundle?
Thanks,
Dean
This topic is covered in an existing question
Since the points that will solve this problem and similar problems I have had, are spread around a bit in that topic, here is my two point cheat sheet:
Point 1: use code like the following to create a pending intent. The choice of flags in the last argument is important:
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Point 2: pending intents are stored in a global system table, and only certain parts of the intent they are created from are part of the "key" that is used to look things up in this table. Extras are not part of the key, so if you want two intents to map to two different pending intents, make sure they are different in some other way, for example having different actions, data, or types.
This example changes the action:
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("whattodo", "showmessage");
// add this:
intent.setAction("showmessage");
(The action can be anything as long as it is different than what you use with the same class elsewhere.
)
There is a good explanation in the latest version of the Javadoc for pending intents., especially this quote I pulled out:
... it is important to know when two Intents are considered to be the same for purposes of retrieving a PendingIntent. A common mistake people make is to create multiple PendingIntent objects with Intents that only vary in their "extra" contents, expecting to get a different PendingIntent each time. This does not happen. The parts of the Intent that are used for matching are the same ones defined by Intent.filterEquals.
I have a service that shows a notification that I wish that will be able to go to a specific activity of my app each time the user presses on it. Usually it would be the last one that the user has shown, but not always.
If the activity was started before, it should return to it, and if not, it should open it inside of the app's task, adding it to the activities tasks.
In addition, on some cases according to the service's logic, I wish to change the notification's intent so that it will target a different activity.
How do i do that? Is it possible without creating a new notification and dismissing the previous one? Is it also possible without creating a new task or an instance of an activity?
No it wouldn't be possible to change the Activity once you have sent the notification.
You can start an Activity on your task stack that is not a problem, check out the notification service in the tutorial here:
http://blog.blundell-apps.com/notification-for-a-user-chosen-time/
You have to set a pending intent on the notification:
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, SecondActivity.class), 0);
// Set the info for the view that shows in the notification panel.
notification.setLatestEventInfo(this, title, text, contentIntent);
You can see the pending intent takes a normal intent "new Intent(this, SecondActivity.class" so if you want specific behaviour (like bringing to the top instead of starting a new activity. Add the flags like you would normally to this intent. i.e. FLAG_ACTIVITY_REORDER_TO_FRONT (something like that)
Since platform version 11, you can build a notification using Notification.Builder. The v4 support library has an equivalent class NotificationCompat.Builder.
You can't change the Activity once you've sent the notification, but you can update the notification with a new Intent. When you create the PendingIntent, use the flag FLAG_CANCEL_CURRENT. When you send the new notification, use the ID of the existing notification when you call NotificationManager.notify().
Also, you should be careful how you start your app. The Status Bar Notifications guide tells you how to set up the back stack.