Context.startService
Intent intent = new Intent(context, MyService.class);
context.startService(intent);
PendingIntent.getService
Intent intent = new Intent(context, MyService.class);
PendingIntent pi = PendingIntent.getService(context, 0, intent, 0);
pi.send();
Questions
When would you start a service with Context.startService vs a PendingIntent?
Why would you use one over the other?
There really is no difference.
Specifically the Context method is used to directly start it where as a PendingIntent is typically used with a notification to fire this intent when it is tapped, which is delayed until the user taps it (typically). However; you wouldn't typically send the PendingIntent directly because that is not what it is for.
A PendingIntent is an Intent that is pending, pending, meaning that its NOT supposed to happen now, but in the near future. Whereas with an Intent, it is sent at the very moment.
If a PendingIntent is not pending when it is used, then it is no longer a PendingIntent and it is infact an Intent. Defeating the purpose entirely.
PendinIntents are very much used for widgets. As the layout of a running widget doesn't "belong" to your code, but it is instead under control of the system, you can't assign directly click listeners to the interface elements. Instead you assign a PendingIntent to those elements (like buttons) so when the user touches them, the PendingIntent is "executed", something like:
// get the widget layout
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.id.widget_layout);
// prepare to listen the clicks on the refresh button
Intent active = new Intent(context, WidgetCode.UpdateService.class);
PendingIntent refreshPendingIntent = PendingIntent.getService(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.buttonWidgetRefresh, refreshPendingIntent);
// send the changes to the widget
AppWidgetManager.getInstance(context).updateAppWidget(appwidgetid, remoteViews);
In this case a button in the widget starts a service. Usually you put extra info in the intent, with putExtras(), so the service will get any needed information to do its job.
Related
So I have a service, in the onCreate() I setup 3 pending intents to start the same service, each having different extra data to specify the action. I'm creating a notification, and I want to use one pending intent as the click action, one as the dismiss action, and the third is for an alarm.
Intent iCancel = new Intent(this, BootService.class);
Intent iAlarm = new Intent(this, BootService.class);
Intent iDismiss = new Intent(this, BootService.class);
// each will have actions
iAlarm.putExtra(INTENT_ACTION, INTENT_ALARM_FIRED);
iCancel.putExtra(INTENT_ACTION, INTENT_CANCEL);
iDismiss.putExtra(INTENT_ACTION, INTENT_DISMISS);
PendingIntent piCancel = PendingIntent.getService(
this,
0,
iCancel,
Intent.FILL_IN_DATA);
mPiAlarm = PendingIntent.getService(this, 0, iAlarm, Intent.FILL_IN_DATA);
PendingIntent piDismiss = PendingIntent.getService(this, 0, iDismiss, Intent.FILL_IN_DATA);
mNotifyBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_action_about)
.setContentTitle(getString(R.string.app_name))
.setContentIntent(piCancel)
.setDeleteIntent(piDismiss);
The problem is all pending intents seem that have the same intent extra data, so when onStartCommand is launched no matter whether the notification was clicked or dismissed or neither, constant INTENT_CANCEL is received from intent.getIntExtra(INTENT_ACTION)
I believe it has something to do with the flags used in PendingIntent.getService(), i'm confused about which to use. I've tried using PendingIntent.FLAG_CANCEL_CURRENT, and UPDATE_CURRENT, neither seem to fix the issue but the result is different, I receive constant INTENT_ALARM_FIRED for every action.
How can I get each pending intent to have its own intent extra data?
Solution
I discovered an exact warning about this scenario right in the PendingIntent doc. http://developer.android.com/reference/android/app/PendingIntent.html
just changed my request codes and it works
This has to do with Intents being considered the same. See PendingIntent for more information.
One way to get around this would be to just vary the requestCode for each Intent. (The 2nd parameter in the getService call)
I find the code below redundant. Am I missing something basic here ? Is there a way to reduce the duplicate code here. Can I use either Intent or PendingIntent object, why both ?
Intent updateUI = new Intent(SENDTOBACKGROUND_SERVICE);
updateUI.putExtra("Signal", God.YELLOW);
sendBroadcast(updateUI);
Intent sendNotification = new Intent(DriverService.this, DriverHome.class);
sendNotification.putExtra("Signal", God.YELLOW);
PendingIntent pIntent = PendingIntent.getActivity(getApplicationContext(), 0, sendNotification, PendingIntent.FLAG_UPDATE_CURRENT);
Notification n = new NotificationCompat.Builder(DriverService.this)
.setContentTitle("Attempting to update location")
.setContentText("Cab #(last) " + currentLocationInText)
.setSmallIcon(R.drawable.yellow).setContentIntent(pIntent).setAutoCancel(true)
.build();
((NotificationManager) DriverService.this.getSystemService(NOTIFICATION_SERVICE)).notify("Taxeeta", R.id.cabLocation, n);
No, you need both. A PendingIntent is a wrapper around an Intent. You can't have a PendingIntent without an Intent to wrap. And you can't put an Intent in a Notification. You need to use a PendingIntent when you want to hand over an Intent to another component, so that the other component can send the Intent for you (as a kind of "proxy") at some point in the future.
As explain in the doc
http://developer.android.com/reference/android/app/PendingIntent.html#getActivity%28android.content.Context,%20int,%20android.content.Intent,%20int%29
"Retrieve a PendingIntent that will start a new activity, like calling Context.startActivity(Intent)" so you need PendingIntent and Intent.
I build custom notification that contain button and i want to listin when user press on it.
The button should not open any activity but only logic staff like change song.
the Code:
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification);
contentView.setTextViewText(R.id.toptext, nowPlayingTitle);
//this not work
Intent intent = new Intent(this, receiver.class);
intent.putExtra("UNIQ", "1");
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(), 234324243, intent, PendingIntent.FLAG_CANCEL_CURRENT)
contentView.setOnClickPendingIntent(R.id.imageButtonPlay,
pendingIntent);
notification.contentView = contentView;
// this is to return to my activity if click somwhere else in notification
Intent notificationIntent = new Intent(this, MYACTIVITY.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
mNotificationManager.notify(NOTIFICATION_ID, notification);
I don't get the hang of the setOnClickPendingIntent what need to be in the second param?
How can i just call a function after user press on the button?
im probably missing something cause i dont understand the receiver side and what happend after user press
You are missing the fact that the button you created actually doesn't belong to your application. It is created in another context, in another process. There is no way it can call your function.
Instead, when the user taps the button, that pending intent is fired. You can catch it by your receiver (in your activity), check some parameters and do the action.
Or you can implement a service and handle this intent in background. I'd prefer this way.
thanks for quick answer. I try using receiver but it never fired.
The code is in the main question and i created for the reciever class the following code:
public class receiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Bundle bundle = intent.getExtras();
}
}
but click on the notification never fire the receiver ( Test on debug mode )
I would like to start an IntentService when pressing on my app widget.
I know how to update the widget with pressing on it but I don't have any idea how I would actually start an IntentService.
This is how to initiate the widget update
Intent intent = new Intent(context, MyWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.update, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
What would I have to change to start an INtentService instead?
ps I also started an activity based on the press, but this shows on the screen - which I do want to avoid.
Many thanks!
Use getService() instead of getBroacast(), and use an Intent that identifies your service.
I am trying to detect when widget button is clicked but none of the Intent extras are showing up in the onReceive method.
onReceive gets called with every click but none of my Intent extras show up.
My code is below: I only hook up the toggle button in on update so not sure if this is correct. None of the extras show up and categories are null even though I set this.
onUpdate(Context context etc):
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.my_widget);
Intent buttonIntent = new Intent(context, MyWidgetProviderClass.class);
buttonIntent.setAction(ACTION_WIDGET_RECEIVER);
buttonIntent.putExtra("BUTTON_CLICKED", "buttonClick");
buttonIntent.putExtra("BUTTON",899);
PendingIntent muPendingIntent = PendingIntent.getBroadcast(context, 0,
buttonIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
buttonIntent.addCategory("buttonclick");
remoteViews.setOnClickPendingIntent(R.id.ToggleImageButton, myPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
onReceive():
intent.getIntExtra("BUTTON",-1); ---> 1
intent.getCategories() --- > null
Try FLAG_UPDATE_CURRENT instead of FLAG_CANCEL_CURRENT.
Also, your code may have a typo: you have muPendingIntent instead of myPendingIntent.
Also also, please do not use buttonclick as a category. Please namespace it (e.g., com.something.whatever.buttonclick), or remove it, as I am not sure why you would need it.
Here is a sample project demonstrating an app widget that, on a click, triggers an update on itself, with an extra (used to supply the app widget IDs).
Android Apparently Does not like re-use of the name ACTION_WIDGET_RECEIVER and removes those parameters. Created another ACTION just for toggle button, registered in the manifest and now the parameters show up.
I found that if the Intent that was used to create the Pending intent has any extras already in it then the new intent's extras are ignored. For example, if you follow the sample in the Android docs for building a Widget like so
Intent toastIntent = new Intent(context, StackWidgetProvider.class);
toastIntent.setAction(StackWidgetProvider.TOAST_ACTION);
toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);
Then the line
toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
Will prevent your new intent's extras from sticking. I removed that line and my new intent worked.